Compare commits

...

159 Commits

Author SHA1 Message Date
Jan Prochazka
31edd48257 v4.8.8-beta.3 2022-04-25 19:03:01 +02:00
Jan Prochazka
8e9a14665b icon 2022-04-25 19:02:24 +02:00
Jan Prochazka
793bc6b494 icon 2022-04-25 19:01:11 +02:00
Jan Prochazka
864b9c9333 icon 2022-04-25 18:37:19 +02:00
Jan Prochazka
fb4dd06578 v4.8.8-beta.2 2022-04-24 23:11:26 +02:00
Jan Prochazka
3c8a0ebd22 transparent icons + icon converter 2022-04-24 23:11:10 +02:00
Jan Prochazka
3a20f24f7c app startup fix 2022-04-24 22:28:04 +02:00
Jan Prochazka
aa8854da93 v4.8.8-beta.1 2022-04-24 15:35:28 +02:00
Jan Prochazka
82ae8e23e0 import db from local files 2022-04-24 15:11:46 +02:00
Jan Prochazka
4d7887a379 export sql dump - can export to files 2022-04-24 15:03:04 +02:00
Jan Prochazka
30b054dbec fixed object destroyed error 2022-04-24 13:20:49 +02:00
Jan Prochazka
df743e8ade changed app icon 2022-04-24 10:11:17 +02:00
Jan Prochazka
7cc70dc5f8 changelog 2022-04-22 20:40:23 +02:00
Jan Prochazka
10491868a0 v4.8.7 2022-04-22 20:27:58 +02:00
Jan Prochazka
8f945dc13f v4.8.7-beta.2 2022-04-21 19:08:57 +02:00
Jan Prochazka
d041f88a47 dockerhost hint on GUI 2022-04-21 19:08:33 +02:00
Jan Prochazka
e5bbf5eca3 dockerhost name support 2022-04-21 18:59:07 +02:00
Jan Prochazka
66da21804b cmd+backspace for delete rows on mac 2022-04-21 17:44:00 +02:00
Jan Prochazka
5315a549b0 import SQL dump fixed 2022-04-21 17:30:16 +02:00
Jan Prochazka
82304f13e7 Merge branch 'master' of github.com:dbgate/dbgate 2022-04-21 10:04:50 +02:00
Jan Prochazka
5bcd5d57ba fix 2022-04-21 10:04:47 +02:00
Jan Prochazka
b6464dc119 Merge branch 'master' of github.com:dbgate/dbgate 2022-04-21 09:48:26 +02:00
Jan Prochazka
4182132dde mssql: fix (varchar(-1) => varchar(max)) 2022-04-21 09:48:21 +02:00
Jan Prochazka
32228d542a fixed crash #268 2022-04-21 08:41:53 +02:00
Jan Prochazka
72f5710dfd removed additional info from issue templates 2022-04-21 08:37:16 +02:00
Jan Prochazka
539c383b21 #270 2022-04-21 08:34:21 +02:00
Jan Prochazka
c3289d09c0 fix system commands 2022-04-21 08:29:41 +02:00
Jan Prochazka
233fed30cb Merge pull request #269 from janpio/patch-1
fix(postgresql): Fix driver UI label "Postgre SQL" => "PostgreSQL"
2022-04-21 08:12:57 +02:00
Jan Piotrowski
63521002cc fix(postgresql): Fix driver UI label "Postgre SQL" => "PostgreSQL" 2022-04-19 17:00:55 +02:00
Jan Prochazka
21642e0a3e v4.8.7-beta.1 2022-04-18 20:00:50 +02:00
Jan Prochazka
4945b71c58 fix dependency 2022-04-18 20:00:11 +02:00
Jan Prochazka
ed0d63d135 import sql dump 2022-04-18 19:59:19 +02:00
Jan Prochazka
2c25669bc7 shortcut fix 2022-04-18 19:37:56 +02:00
Jan Prochazka
0c94145b18 runner messages dialog 2022-04-18 19:27:37 +02:00
Jan Prochazka
95fb5d51c5 import SQL dump 2022-04-18 14:52:01 +02:00
Jan Prochazka
a75e931fd5 import SQL dump POC 2022-04-18 14:22:16 +02:00
Jan Prochazka
c7c667bbe0 Merge branch 'master' into develop 2022-04-18 11:10:12 +02:00
Jan Prochazka
97eff2b113 fix keybindings 2022-04-18 11:08:15 +02:00
Jan Prochazka
0122b0accc mac - fixed command+C, command+v 2022-04-18 10:42:50 +02:00
Jan Prochazka
6c718981d6 mysql dumper POC 2022-04-15 20:12:40 +02:00
Jan Prochazka
f78d37159e v4.8.6 2022-04-14 17:34:55 +02:00
Jan Prochazka
bf1881595c don't fail whole pipeline if one platforms fails 2022-04-14 17:19:03 +02:00
Jan Prochazka
4e8c92eb36 v4.8.5 2022-04-14 16:52:40 +02:00
Jan Prochazka
34a0cb095b build fix 2022-04-14 16:52:10 +02:00
Jan Prochazka
f606d0c41c fix 2022-04-14 16:13:35 +02:00
Jan Prochazka
8002d734bc fix build script 2022-04-14 16:03:33 +02:00
Jan Prochazka
be6d572ce2 changelog 2022-04-14 15:40:07 +02:00
Jan Prochazka
c3baa88d9e v4.8.4 2022-04-14 15:26:47 +02:00
Jan Prochazka
5ef06abd1a v4.8.4-beta.8 2022-04-14 14:47:51 +02:00
Jan Prochazka
e86a34ab53 apple notarize 2022-04-14 14:47:29 +02:00
Jan Prochazka
c2b715e3f7 v4.8.4-beta.7 2022-04-14 13:49:12 +02:00
Jan Prochazka
666f1a3159 app build 2022-04-14 13:48:52 +02:00
Jan Prochazka
1ee66047d4 Merge branch 'develop' 2022-04-14 13:45:41 +02:00
Jan Prochazka
8032bf272b product name 2022-04-14 13:27:26 +02:00
Jan Prochazka
d52cfadfc4 mac menu 2022-04-14 13:21:06 +02:00
Jan Prochazka
374c820567 invoke commands from menu on mac 2022-04-14 12:33:03 +02:00
Jan Prochazka
cc639df566 handle ctrl & command key 2022-04-14 11:35:09 +02:00
Jan Prochazka
5a42e8e963 app exit - mac 2022-04-14 11:05:33 +02:00
Jan Prochazka
0187bb74ee v4.8.4-beta.6 2022-04-14 08:10:33 +02:00
Jan Prochazka
bf4b7beadb mac universal build 2022-04-14 08:10:18 +02:00
Jan Prochazka
b254c90f33 v4.8.4-beta.5 2022-04-14 08:08:30 +02:00
Jan Prochazka
89fdfbe8c1 arm build 2022-04-14 08:08:17 +02:00
Jan Prochazka
a37c81be88 Merge pull request #267 from moalamri/master
Fixes misplaced tab close icon
2022-04-14 07:43:19 +02:00
M. Hassan
abe9694d05 Fixes misplaced tab close icon
To fix #260
I think there's no need to change the close button to `close-button-right` since the `file-name` span would flex correctly same as `db-name`
2022-04-14 01:11:00 +03:00
Jan Prochazka
c55c1f3aaf v4.8.4-beta.4 2022-04-12 22:35:24 +02:00
Jan Prochazka
ce6b750a1c build only mac 2022-04-12 22:35:10 +02:00
Jan Prochazka
f83d45356f v4.8.4-beta.3 2022-04-12 21:53:16 +02:00
Jan Prochazka
589ff1ad38 apple code signing 2022-04-12 21:52:56 +02:00
Jan Prochazka
ba97f50273 Merge branch 'master' of github.com:dbgate/dbgate 2022-04-11 20:48:42 +02:00
Jan Prochazka
e52fbd5034 keyboard fixes 2022-04-11 20:48:39 +02:00
Jan Prochazka
e29d3a6143 v4.8.4-beta.2 2022-04-11 20:04:28 +02:00
Jan Prochazka
7313fa16f6 #264 fix 2022-04-11 20:03:56 +02:00
Jan Prochazka
18437d1be7 Revert "mac code signing"
This reverts commit 5135b985c9.
2022-04-10 19:24:40 +02:00
Jan Prochazka
2b39c85918 v4.8.4-beta.1 2022-04-10 18:26:54 +02:00
Jan Prochazka
5135b985c9 mac code signing 2022-04-10 18:26:15 +02:00
Jan Prochazka
05619faa7a fixed opening files on mac #206 #243 2022-04-10 10:57:31 +02:00
Jan Prochazka
12a638af3b custom window title works on mac 2022-04-10 09:41:55 +02:00
Jan Prochazka
38e05bf8e0 recent database switch in main menu 2022-04-10 08:33:13 +02:00
Jan Prochazka
6d92de6930 mac main menu 2022-04-10 08:30:39 +02:00
Jan Prochazka
90f8d349fc mac specific keyboard shortcuts #199 2022-04-09 20:00:26 +02:00
Jan Prochazka
5379e86d6e build fix 2022-04-09 09:30:01 +02:00
Jan Prochazka
85e449953f query splitter extracted into separate repository 2022-04-07 08:17:38 +02:00
Jan Prochazka
30e52723dd changelog 2022-04-05 22:15:06 +02:00
Jan Prochazka
467918bcbb v4.8.3 2022-04-05 22:09:15 +02:00
Jan Prochazka
73d17504c1 v4.8.3-beta.3 2022-04-04 21:09:11 +02:00
Jan Prochazka
7a3007deb2 clickable fk links in column manager 2022-04-04 21:08:43 +02:00
Jan Prochazka
2a46ff78bb fix 2022-04-04 20:53:39 +02:00
Jan Prochazka
fa193f0e57 theme plugins info 2022-04-04 20:50:45 +02:00
Jan Prochazka
f702513bb9 #244 2022-04-04 19:46:18 +02:00
Jan Prochazka
72462376b1 fix 2022-04-04 19:23:37 +02:00
Jan Prochazka
a46ef7f0d0 v4.8.3-beta.2 2022-04-03 13:13:49 +02:00
Jan Prochazka
9f5013c6da handle plugin icon 2022-04-03 13:13:27 +02:00
Jan Prochazka
5ea6c56752 fixed theme selector 2022-04-03 11:21:38 +02:00
Jan Prochazka
cbb38b8edc dataty pe in column manager and in form view 2022-04-03 10:35:29 +02:00
Jan Prochazka
045f6c6a47 SSH reconnect #253 2022-04-03 10:02:32 +02:00
Jan Prochazka
334ab504cf process api resp 2022-04-03 09:53:02 +02:00
Jan Prochazka
66db28010c api error handling 2022-04-03 09:50:24 +02:00
Jan Prochazka
807392fc57 fix 2022-04-03 09:41:19 +02:00
Jan Prochazka
1a32d88312 ssh forward moved to extra process #253 2022-04-03 09:40:46 +02:00
Jan Prochazka
1a76cc0979 v4.8.3-beta.1 2022-04-03 07:34:11 +02:00
Jan Prochazka
bcdaf84739 api logging 2022-04-03 07:33:53 +02:00
Jan Prochazka
39296a852e #254 keyboard shortcuts in main menu 2022-03-31 16:03:38 +02:00
Jan Prochazka
1a035ca168 #254 tab navigation in datagrid 2022-03-31 16:01:06 +02:00
Jan Prochazka
c0b365602b read json lines field values 2022-03-31 15:35:38 +02:00
Jan Prochazka
5aac142e4c fixed evaluated filters 2022-03-31 15:15:15 +02:00
Jan Prochazka
25380ee0a8 v4.8.2 2022-03-31 11:26:37 +02:00
Jan Prochazka
0b3b18ceda v4.8.2-beta.2 2022-03-31 10:37:11 +02:00
Jan Prochazka
9cecebe8bc redis key tab name & icon 2022-03-31 10:36:55 +02:00
Jan Prochazka
c0227ecce1 changelog 2022-03-31 09:57:45 +02:00
Jan Prochazka
1d37950b37 v4.8.2-beta.1 2022-03-31 09:49:39 +02:00
Jan Prochazka
fe277f5ffa redis search keys 2022-03-31 09:44:50 +02:00
Jan Prochazka
1b52a2a0fc v4.8.1 2022-03-29 22:00:05 +02:00
Jan Prochazka
a062073a5f changelog 2022-03-29 21:56:51 +02:00
Jan Prochazka
44e52dfa9c v4.8.1-beta.1 2022-03-29 21:44:14 +02:00
Jan Prochazka
0323264bbd fixed crash 2022-03-29 21:43:50 +02:00
Jan Prochazka
aacedba450 v4.8.0 2022-03-28 19:30:42 +02:00
Jan Prochazka
aac768b158 changelog 2022-03-28 19:11:22 +02:00
Jan Prochazka
d4a7ae13e1 v4.8.0-beta.1 2022-03-28 19:06:20 +02:00
Jan Prochazka
b8206a7a02 open dev tools on crash, when not reloading 2022-03-28 19:05:15 +02:00
Jan Prochazka
4c2f6c9b65 upload ndjson to web app 2022-03-28 18:58:02 +02:00
Jan Prochazka
d58d46feac v4.7.5-beta.1 2022-03-27 20:37:35 +02:00
Jan Prochazka
760edc7eca redis - removed experimental status 2022-03-27 20:37:17 +02:00
Jan Prochazka
7c0f33383f ndjson direct support 2022-03-27 20:35:30 +02:00
Jan Prochazka
a20a34680d redis: hide write operations when connection is readonly 2022-03-27 18:39:42 +02:00
Jan Prochazka
0e8166577f redis: support readonly and database url 2022-03-27 18:34:22 +02:00
Jan Prochazka
11c82b1aac redis json view 2022-03-27 17:45:05 +02:00
Jan Prochazka
61f24c3408 json view in redis 2022-03-27 17:34:02 +02:00
Jan Prochazka
e25657bd43 redis: support for redis streams 2022-03-27 17:16:17 +02:00
Jan Prochazka
4bd7cd26d0 redis db context menu 2022-03-27 13:42:08 +02:00
Jan Prochazka
e06894372f redis: rename key command 2022-03-27 13:06:39 +02:00
Jan Prochazka
1f0ae98c88 redis export keys 2022-03-27 12:57:44 +02:00
Jan Prochazka
c0fdcf2fd1 redis: execute commands 2022-03-26 10:36:44 +01:00
Jan Prochazka
8d31130737 redis query splitter 2022-03-26 09:56:28 +01:00
Jan Prochazka
9e3991556a delete branch operation 2022-03-26 08:12:56 +01:00
Jan Prochazka
fc08353225 changelog 2022-03-25 18:38:51 +01:00
Jan Prochazka
25556f0d3e changelog 2022-03-25 18:35:56 +01:00
Jan Prochazka
0fb3817af6 v4.7.4 2022-03-25 18:23:08 +01:00
Jan Prochazka
d8e840f127 v4.7.4-beta.17 2022-03-24 18:59:35 +01:00
Jan Prochazka
4270722557 v4.7.4-alpha.16 2022-03-24 18:59:15 +01:00
Jan Prochazka
b3cfff0ae8 redis marked as experimental 2022-03-24 18:58:57 +01:00
Jan Prochazka
2f5f0ab54c Revert "don't include redis in output bundle"
This reverts commit 074a75075d.
2022-03-24 18:52:34 +01:00
Jan Prochazka
4c856c5e36 refresh after delete 2022-03-24 18:51:46 +01:00
Jan Prochazka
5c8ae85c54 redis: refresh after create key 2022-03-24 18:49:45 +01:00
Jan Prochazka
5b39576e61 redis: add key modal style 2022-03-24 18:41:39 +01:00
Jan Prochazka
735c48902f redis: reload keys 2022-03-24 18:32:17 +01:00
Jan Prochazka
613ac3f0e5 redis: delete key 2022-03-24 18:24:10 +01:00
Jan Prochazka
1aecda6d9f add new key works 2022-03-24 17:58:54 +01:00
Jan Prochazka
38dfad4dfc #246 omit sngle-database dbs in ctrl+p seach 2022-03-24 16:25:36 +01:00
Jan Prochazka
25ae5bf048 fuzzy search #246 2022-03-24 15:48:21 +01:00
Jan Prochazka
f3bfe58c58 dbgate-serve package refs 2022-03-24 14:09:34 +01:00
Jan Prochazka
ff714b1f8a v4.7.4-alpha.15 2022-03-24 14:02:15 +01:00
Jan Prochazka
93552585f7 fixes 2022-03-24 14:02:02 +01:00
Jan Prochazka
ec7641dbd6 v4.7.4-alpha.14 2022-03-24 09:55:57 +01:00
Jan Prochazka
e2308f501b renamed package dbgate to dbgate-serve 2022-03-24 09:55:39 +01:00
Jan Prochazka
45277e34c9 try to fix npm build 2022-03-24 09:49:14 +01:00
Jan Prochazka
ba77b32e9a v4.7.4-beta.13 2022-03-24 09:14:04 +01:00
Jan Prochazka
c05ef42bf9 v4.7.4-alpha.12 2022-03-24 09:13:49 +01:00
Jan Prochazka
b0e0197346 fixed docker container 2022-03-24 09:13:33 +01:00
Jan Prochazka
4c411b048d fixed npm dist subprocess problem 2022-03-24 09:00:51 +01:00
181 changed files with 2855 additions and 1467 deletions

View File

@@ -28,9 +28,3 @@ If applicable, add screenshots to help explain your problem.
- Install source [e.g. installer/SNAP/Docker/NPM]
- Type - Web/Application
- Database engine: [e.g. MySQL/PostgreSQL/SQL Server]
**Additional info**
You could provide additional context, to better understand your case.
- How looks your typical DbGate usage?
- What other DB software do you use?
- Anything else you think might be helpful

View File

@@ -15,9 +15,3 @@ A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional info**
You could provide additional context, to better understand your case.
- How looks your typical DbGate usage?
- What other DB software do you use?
- Anything else you think might be helpful

View File

@@ -16,9 +16,3 @@ App Version [help -> about]:
**Screenshot [if appropriate]**:
A screenshot of the app if that helps
**Additional info**
You could provide additional context, to better understand your case.
- How looks your typical DbGate usage?
- What other DB software do you use?
- Anything else you think might be helpful

View File

@@ -10,9 +10,10 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# os: [ubuntu-18.04, windows-2016]
os: [macOS-10.15, windows-2022, ubuntu-18.04]
# os: [macOS-10.15]
steps:
- name: Context
@@ -46,9 +47,16 @@ jobs:
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: Save snap login
if: matrix.os == 'ubuntu-18.04'
run: 'echo "$SNAPCRAFT_LOGIN" > snapcraft.login'

View File

@@ -14,6 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# os: [ubuntu-18.04, windows-2016]
os: [macOS-10.15, windows-2022, ubuntu-18.04]
@@ -52,9 +53,16 @@ jobs:
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: generatePadFile
run: |
yarn generatePadFile
@@ -83,8 +91,8 @@ jobs:
cp app/dist/*.exe artifacts/dbgate-latest.exe || true
cp app/dist/*win_x64.zip artifacts/dbgate-windows-latest.zip || true
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-latest-arm64.zip || true
cp app/dist/*-mac_x64.dmg artifacts/dbgate-latest.dmg || true
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-latest-arm64.dmg || true
cp app/dist/*-mac_universal.dmg artifacts/dbgate-latest.dmg || true
cp app/dist/*-mac_x64.dmg artifacts/dbgate-latest-x64.dmg || true
mv app/dist/*.exe artifacts/ || true
mv app/dist/*.zip artifacts/ || true

View File

@@ -79,21 +79,21 @@ jobs:
run: |
npm publish
- name: Publish query-splitter
working-directory: packages/query-splitter
run: |
npm publish
- name: Publish web
working-directory: packages/web
run: |
npm publish
- name: Publish dbgate
- name: Publish dbgate (obsolete)
working-directory: packages/dbgate
run: |
npm publish
- name: Publish dbgate-serve
working-directory: packages/serve
run: |
npm publish
- name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv
run: |

View File

@@ -31,11 +31,6 @@ jobs:
run: |
cd packages/filterparser
yarn test:ci
- name: Query spliiter tests
if: always()
run: |
cd packages/query-splitter
yarn test:ci
- uses: tanmen/jest-reporter@v1
if: always()
with:
@@ -48,12 +43,6 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/query-splitter/result.json
action-name: Query splitter test results
services:
postgres:

View File

@@ -1,5 +1,68 @@
# ChangeLog
Builds:
- docker - build
- npm - npm package dbgate-serve
- app - classic electron app
- mac - application for macOS
- linux - application for linux
- win - application for Windows
### 4.8.7
- ADDED: MySQL dump/backup database
- ADDED: Import SQL dump from file or from URL
- FIXED(mac): Fixed Cmd+C, Cmd+V, Cmd+X - shortcuts for copy/cut/paste #270
- FIXED(mac): Some minor issues on macOS
- FIXED: Analysing MS SQL nvarchar(max)
- ADDED: Support for dockerhost network name under docker #271
### 4.8.4
- FIXED(mac): Fixed build for macOS arm64 #259
- FIXED(mac): Fixed opening SQLite files on macOS #243
- FIXED(mac): Fixed opening PEM certificates on macOS #206
- FIXED(mac): Fixed handling Command key on macOS
- FIXED(mac): Fixed system menu on macOS
- FIXED(mac): Fixed reopening main window on macOS
- CHANGED: Shortcut for net query is now Ctrl+T or Command+T on macOS, former it was Ctrl+Q
- FIXED: Fixed misplaced tab close icon #260
- ADDED: Added menu command "Tools/Change to recent database"
### 4.8.3
- FIXED: filters in query result and NDJSON/archive viewer
- ADDED: Added select values from query result and NDJSON/archive viewer
- ADDED: tab navigation in datagrid #254
- ADDED: Keyboard shortcuts added to help menu #254
- ADDED: API logging (run enableApiLog() in developers console to enable logging)
- ADDED: SSH reconnect + moved SSH forward into separate fork #253
- ADDED: Data type + reference link in column manager
- FIXED(win,linux,mac): Unable to change theme after installing plugin #244
### 4.8.2
- ADDED: implemented missing redis search key logic
### 4.8.1
- FIXED: fixed crash after disconnecting from all DBs
### 4.8.0
- ADDED: Redis support (support stream type), removed experimental status
- ADDED: Redis readonly support
- ADDED: Explicit NDJSON support, when opening NDJSON/JSON lines file, table data are immediately shown, without neccesarity to import
- ADDED(win,linux,mac): Opening developer tools when crashing without reload app
### 4.7.4
- ADDED: Experimental Redis support (full support is planned to version 4.8.0)
- ADDED: Read-only connections
- FIXED: MongoDB filters
- ADDED: MongoDB column value selection
- ADDED: App related queries
- ADDED: Fuzzy search #246
- ADDED(docker, npm): New permissions
- FIXED(npm): NPM build no longer allocates additonal ports
- CHANGED(npm): renamed NPM package dbgate => dbgate-serve
- CHANGED(docker): custom JavaScripts and connections defined in scripts are now prohibited by default, use SHELL_CONNECTION and SHELL_SCRIPTING environment variables for allowing this
- ADDED(docker, npm): Better documentation of environment variables configuration, https://dbgate.org/docs/env-variables.html
- ADDED(docker): support for multiple users with different permissions
- ADDED(docker): logout operation
### 4.7.3
- CHANGED: Export menu redesign, quick export menu merged with old export menu
- REMOVED: Quick export menu

View File

@@ -1,4 +1,4 @@
[![NPM version](https://img.shields.io/npm/v/dbgate.svg)](https://www.npmjs.com/package/dbgate)
[![NPM version](https://img.shields.io/npm/v/dbgate-serve.svg)](https://www.npmjs.com/package/dbgate-serve)
![GitHub All Releases](https://img.shields.io/github/downloads/dbgate/dbgate/total)
[![dbgate](https://snapcraft.io/dbgate/badge.svg)](https://snapcraft.io/dbgate)
[![dbgate](https://snapcraft.io/dbgate/trending.svg?name=0)](https://snapcraft.io/dbgate)
@@ -16,13 +16,14 @@ DbGate is licensed under MIT license and is completely free.
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
* **Download** application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
## Supported databases
* MySQL
* PostgreSQL
* SQL Server
* MongoDB
* Redis
* SQLite
* Amazon Redshift
* CockroachDB
@@ -63,6 +64,7 @@ DbGate is licensed under MIT license and is completely free.
* SQL code completion
* Add SQL LEFT/INNER/RIGHT join utility
* Mongo JavaScript editor, execute Mongo script (with NodeJs syntax)
* Redis tree view, generate script from keys, run Redis script
* Runs as application for Windows, Linux and Mac. Or in Docker container on server and in web Browser on client.
* Import, export from/to CSV, Excel, JSON, XML
* Free table editor - quick table data editing (cleanup data after import/before export, prototype tables etc.)

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key><true/>
<key>com.apple.security.cs.allow-jit</key><true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
<key>com.apple.security.files.user-selected.read-only</key><true/>
<key>com.apple.security.files.user-selected.read-write</key><true/>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -15,18 +15,23 @@
"url": "https://github.com/dbgate/dbgate.git"
},
"build": {
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
"artifactName": "dbgate-${version}-${os}_${arch}.${ext}",
"appId": "org.dbgate",
"productName": "DbGate",
"afterSign": "electron-builder-notarize",
"mac": {
"category": "database",
"icon": "icon512.png",
"hardenedRuntime": true,
"entitlements": "entitlements.mac.plist",
"entitlementsInherit": "entitlements.mac.plist",
"publish": [
"github"
],
"target": {
"target": "default",
"arch": [
"arm64",
"universal",
"x64"
]
}
@@ -103,7 +108,8 @@
"copyfiles": "^2.2.0",
"cross-env": "^6.0.3",
"electron": "13.6.3",
"electron-builder": "22.14.5"
"electron-builder": "22.14.5",
"electron-builder-notarize": "^1.4.0"
},
"optionalDependencies": {
"better-sqlite3": "7.4.5",

View File

@@ -20,6 +20,10 @@ const { settings } = require('cluster');
const configRootPath = path.join(app.getPath('userData'), 'config-root.json');
let initialConfig = {};
let apiLoaded = false;
let mainModule;
const isMac = () => os.platform() == 'darwin';
try {
initialConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' }));
@@ -41,6 +45,7 @@ try {
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let mainMenu;
let runCommandOnLoad = null;
log.transports.file.level = 'debug';
autoUpdater.logger = log;
@@ -49,30 +54,72 @@ autoUpdater.logger = log;
let commands = {};
function commandItem(id) {
function formatKeyText(keyText) {
if (!keyText) {
return keyText;
}
if (os.platform() == 'darwin') {
return keyText.replace('CtrlOrCommand+', 'Command+');
}
return keyText.replace('CtrlOrCommand+', 'Ctrl+');
}
function commandItem(item) {
const id = item.command;
const command = commands[id];
if (item.skipInApp) {
return { skip: true };
}
return {
id,
label: command ? command.menuName || command.toolbarName || command.name : id,
accelerator: command ? command.keyText : undefined,
accelerator: formatKeyText(command ? command.keyText : undefined),
enabled: command ? command.enabled : false,
click() {
mainWindow.webContents.send('run-command', id);
if (mainWindow) {
mainWindow.webContents.send('run-command', id);
} else {
runCommandOnLoad = id;
createWindow();
}
},
};
}
function buildMenu() {
const template = _cloneDeepWith(mainMenuDefinition, item => {
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true }), item => {
if (item.divider) {
return { type: 'separator' };
}
if (item.command) {
return commandItem(item.command);
return commandItem(item);
}
});
template = _cloneDeepWith(template, item => {
if (Array.isArray(item) && item.find(x => x.skip)) {
return item.filter(x => x && !x.skip);
}
});
if (isMac()) {
template = [
{
label: 'DbGate',
submenu: [
commandItem({ command: 'about.show' }),
{ role: 'services' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ role: 'quit' },
],
},
...template,
];
}
return Menu.buildFromTemplate(template);
}
@@ -86,15 +133,21 @@ ipcMain.on('update-commands', async (event, arg) => {
// rebuild menu
if (menu.label != command.text || menu.accelerator != command.keyText) {
mainMenu = buildMenu();
mainWindow.setMenu(mainMenu);
Menu.setApplicationMenu(mainMenu);
// mainWindow.setMenu(mainMenu);
return;
}
menu.enabled = command.enabled;
}
});
ipcMain.on('close-window', async (event, arg) => {
mainWindow.close();
ipcMain.on('quit-app', async (event, arg) => {
if (isMac()) {
app.quit();
} else {
mainWindow.close();
}
});
ipcMain.on('set-title', async (event, arg) => {
mainWindow.setTitle(arg);
@@ -102,7 +155,19 @@ ipcMain.on('set-title', async (event, arg) => {
ipcMain.on('open-link', async (event, arg) => {
electron.shell.openExternal(arg);
});
ipcMain.on('open-dev-tools', () => {
mainWindow.webContents.openDevTools();
});
ipcMain.on('app-started', async (event, arg) => {
if (runCommandOnLoad) {
mainWindow.webContents.send('run-command', runCommandOnLoad);
runCommandOnLoad = null;
}
});
ipcMain.on('window-action', async (event, arg) => {
if (!mainWindow) {
return;
}
switch (arg) {
case 'minimize':
mainWindow.minimize();
@@ -138,6 +203,23 @@ ipcMain.on('window-action', async (event, arg) => {
case 'zoomreset':
mainWindow.webContents.zoomLevel = 0;
break;
// edit
case 'undo':
mainWindow.webContents.undo();
break;
case 'redo':
mainWindow.webContents.redo();
break;
case 'cut':
mainWindow.webContents.cut();
break;
case 'copy':
mainWindow.webContents.copy();
break;
case 'paste':
mainWindow.webContents.paste();
break;
}
});
@@ -161,7 +243,8 @@ function fillMissingSettings(value) {
...value,
};
if (value['app.useNativeMenu'] !== true && value['app.useNativeMenu'] !== false) {
res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
res['app.useNativeMenu'] = false;
// res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
}
return res;
}
@@ -236,25 +319,29 @@ function createWindow() {
// mainWindow.webContents.toggleDevTools();
}
const apiPackage = path.join(
__dirname,
process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js'
);
if (!apiLoaded) {
const apiPackage = path.join(
__dirname,
process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js'
);
global.API_PACKAGE = apiPackage;
global.NATIVE_MODULES = path.join(__dirname, 'nativeModules');
global.API_PACKAGE = apiPackage;
global.NATIVE_MODULES = path.join(__dirname, 'nativeModules');
// console.log('global.API_PACKAGE', global.API_PACKAGE);
const api = require(apiPackage);
// console.log(
// 'REQUIRED',
// path.resolve(
// path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js')
// )
// );
const main = api.getMainModule();
main.initializeElectronSender(mainWindow.webContents);
main.useAllControllers(null, electron);
// console.log('global.API_PACKAGE', global.API_PACKAGE);
const api = require(apiPackage);
// console.log(
// 'REQUIRED',
// path.resolve(
// path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js')
// )
// );
const main = api.getMainModule();
main.useAllControllers(null, electron);
mainModule = main;
apiLoaded = true;
}
mainModule.setElectronSender(mainWindow.webContents);
loadMainWindow();
@@ -264,6 +351,7 @@ function createWindow() {
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
mainModule.setElectronSender(null);
});
}
@@ -283,7 +371,7 @@ app.on('ready', onAppReady);
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
if (!isMac()) {
app.quit();
}
});

View File

@@ -1,4 +1,4 @@
module.exports = [
module.exports = ({ editMenu }) => [
{
label: 'File',
submenu: [
@@ -17,7 +17,7 @@ module.exports = [
{ command: 'group.saveAs', hideDisabled: true },
{ divider: true },
{ command: 'file.exit', hideDisabled: true },
{ command: 'app.logout', hideDisabled: true },
{ command: 'app.logout', hideDisabled: true, skipInApp: true },
],
},
{
@@ -34,6 +34,20 @@ module.exports = [
],
},
editMenu
? {
label: 'Edit',
submenu: [
{ command: 'edit.undo' },
{ command: 'edit.redo' },
{ divider: true },
{ command: 'edit.cut' },
{ command: 'edit.copy' },
{ command: 'edit.paste' },
],
}
: null,
// {
// label: 'Edit',
// submenu: [
@@ -62,6 +76,7 @@ module.exports = [
submenu: [
{ command: 'database.search', hideDisabled: true },
{ command: 'commandPalette.show', hideDisabled: true },
{ command: 'database.switch', hideDisabled: true },
{ divider: true },
{ command: 'sql.generator', hideDisabled: true },
{ command: 'file.import', hideDisabled: true },
@@ -76,6 +91,7 @@ module.exports = [
{ command: 'app.openIssue', hideDisabled: true },
{ command: 'app.openSponsoring', hideDisabled: true },
{ divider: true },
{ command: 'settings.commands', hideDisabled: true },
{ command: 'tabs.changelog', hideDisabled: true },
{ command: 'about.show', hideDisabled: true },
],

View File

@@ -7,6 +7,27 @@
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
"@babel/code-frame@^7.0.0":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
dependencies:
"@babel/highlight" "^7.16.7"
"@babel/helper-validator-identifier@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
"@babel/highlight@^7.16.7":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3"
integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==
dependencies:
"@babel/helper-validator-identifier" "^7.16.7"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@develar/schema-utils@~2.6.5":
version "2.6.5"
resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6"
@@ -113,6 +134,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.2.tgz#00fe4d1686d5f6cf3a2f2e9a0eef42594d06abfc"
integrity sha512-fqtSN5xn/bBzDxMT77C1rJg6CsH/R49E7qsGuvdPJa20HtV5zSTuLJPNfnlyVH3wauKnkHdLggTVkOW/xP9oQg==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
"@types/plist@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01"
@@ -243,6 +269,13 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -459,7 +492,7 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e"
integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==
chalk@^2.4.2:
chalk@^2.0.0, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -829,6 +862,15 @@ ejs@^3.1.6:
dependencies:
jake "^10.6.1"
electron-builder-notarize@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/electron-builder-notarize/-/electron-builder-notarize-1.4.0.tgz#9e3609935eb70bf08f64fba255c3e4c43b0058e7"
integrity sha512-5CPVlzkG+SofK3VU3E6HKmdXW6Uu6q5WWvzXX6diLAlAy9qJsR0n99aNztVKKsPl6yjEbvT+MUl4ci0YCwOBRA==
dependencies:
electron-notarize "^1.1.1"
js-yaml "^3.14.0"
read-pkg-up "^7.0.0"
electron-builder@22.14.5:
version "22.14.5"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.14.5.tgz#3a25547bd4fe3728d4704da80956a794c5c31496"
@@ -852,6 +894,14 @@ electron-log@^4.4.1:
resolved "https://registry.yarnpkg.com/electron-log/-/electron-log-4.4.1.tgz#28ebeb474eccba2ebf194a96c40d6328e5353e4d"
integrity sha512-nK/DwxPLtwWbggPCm27eMQhYHc3gzoZ+cokBK99diO4WsZJKrv5l44EUW8mRfWpmC8ZubnMyp6GTUIJyTc9AJA==
electron-notarize@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-1.2.1.tgz#347c18eca8e29dddadadee511b870c13d4008baf"
integrity sha512-u/ECWhIrhkSQpZM4cJzVZ5TsmkaqrRo5LDC/KMbGF0sPkm53Ng59+M0zp8QVaql0obfJy9vlVT+4iOkAi2UDlA==
dependencies:
debug "^4.1.1"
fs-extra "^9.0.1"
electron-osx-sign@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.5.0.tgz#fc258c5e896859904bbe3d01da06902c04b51c3a"
@@ -922,6 +972,13 @@ env-paths@^2.2.0:
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
@@ -947,6 +1004,11 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
expand-template@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
@@ -1003,6 +1065,14 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-yarn-workspace-root@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
@@ -1073,6 +1143,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -1206,6 +1281,18 @@ has-yarn@^2.1.0:
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hosted-git-info@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961"
@@ -1271,6 +1358,11 @@ ini@^1.3.4, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-ci@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
@@ -1285,6 +1377,13 @@ is-ci@^3.0.0:
dependencies:
ci-info "^3.2.0"
is-core-module@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
dependencies:
has "^1.0.3"
is-docker@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
@@ -1384,6 +1483,19 @@ jake@^10.6.1:
filelist "^1.0.1"
minimatch "^3.0.4"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.14.0:
version "3.14.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -1396,6 +1508,11 @@ json-buffer@3.0.0:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
json-parse-even-better-errors@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -1455,6 +1572,18 @@ lazy-val@^1.0.4, lazy-val@^1.0.5:
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.5.tgz#6cf3b9f5bc31cee7ee3e369c0832b7583dcd923d"
integrity sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==
lines-and-columns@^1.1.6:
version "1.2.4"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
lodash.clonedeepwith@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4"
@@ -1650,6 +1779,16 @@ noms@0.0.0:
inherits "^2.0.1"
readable-stream "~1.0.31"
normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-url@^4.1.0:
version "4.5.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
@@ -1713,6 +1852,25 @@ p-cancelable@^1.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
package-json@^6.3.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
@@ -1723,6 +1881,16 @@ package-json@^6.3.0:
registry-url "^5.0.0"
semver "^6.2.0"
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
patch-package@^6.4.7:
version "6.4.7"
resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148"
@@ -1742,6 +1910,11 @@ patch-package@^6.4.7:
slash "^2.0.0"
tmp "^0.0.33"
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -1757,6 +1930,11 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
@@ -1879,6 +2057,25 @@ read-config-file@6.2.0:
json5 "^2.2.0"
lazy-val "^1.0.4"
read-pkg-up@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
dependencies:
find-up "^4.1.0"
read-pkg "^5.2.0"
type-fest "^0.8.1"
read-pkg@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
dependencies:
"@types/normalize-package-data" "^2.4.0"
normalize-package-data "^2.5.0"
parse-json "^5.0.0"
type-fest "^0.6.0"
readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@@ -1930,6 +2127,15 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
resolve@^1.10.0:
version "1.22.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
dependencies:
is-core-module "^2.8.1"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
@@ -2002,7 +2208,7 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -2115,11 +2321,42 @@ source-map@^0.6.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.11"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
stat-mode@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465"
@@ -2202,6 +2439,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
tar-fs@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
@@ -2313,6 +2555,16 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
@@ -2391,6 +2643,14 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
verror@^1.10.0:
version "1.10.1"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb"

47
convert-icons.sh Executable file
View File

@@ -0,0 +1,47 @@
# magick icon.svg -resize 1024x1024 -transparent white -background transparent app/icon1024.png
# magick app/icon1024.png -resize 512x512 app/icon512.png
# magick app/icon1024.png -resize 256x256 app/icon.png
# magick app/icon1024.png -resize 32x32 app/icon32.png
# magick icon.svg -define icon:auto-resize="256,128,96,64,48,32,16" -transparent white -background transparent app/icon.ico
# # magick icon.svg -resize 512x512 -transparent white -background transparent app/icon512.png
# # magick icon.svg -resize 256x256 -transparent white -background transparent app/icon.png
# # magick icon.svg -resize 32x32 -transparent white -background transparent app/icon32.png
# # magick icon.svg -define icon:auto-resize="256,128,96,64,48,32,16" -transparent white -background transparent app/icon.ico
# magick app/icon1024.png -resize 512x512 app/icons/512x512.png
# magick app/icon1024.png -resize 256x256 app/icons/256x256.png
# magick app/icon1024.png -resize 128x128 app/icons/128x128.png
# magick app/icon1024.png -resize 64x64 app/icons/64x64.png
# magick app/icon1024.png -resize 48x48 app/icons/48x48.png
# magick app/icon1024.png -resize 32x32 app/icons/32x32.png
# magick app/icon1024.png -resize 16x16 app/icons/16x16.png
# # magick icon.svg -resize 16x16 -transparent white -background transparent app/icons/16x16.png
# # magick icon.svg -resize 32x32 -transparent white -background transparent app/icons/32x32.png
# # magick icon.svg -resize 48x48 -transparent white -background transparent app/icons/48x48.png
# # magick icon.svg -resize 64x64 -transparent white -background transparent app/icons/64x64.png
# # magick icon.svg -resize 128x128 -transparent white -background transparent app/icons/128x128.png
# # magick icon.svg -resize 256x256 -transparent white -background transparent app/icons/256x256.png
# # magick icon.svg -resize 512x512 -transparent white -background transparent app/icons/512x512.png
# magick icon.svg -resize 1024x1024 icon.png
# magick icon.svg -resize 1024x1024 -transparent white -background transparent icon.png
magick icon.png -resize 512x512 app/icon512.png
magick icon.png -resize 256x256 app/icon.png
magick icon.png -resize 32x32 app/icon32.png
magick icon.png -define icon:auto-resize="256,128,96,64,48,32,16" app/icon.ico
magick icon.png -resize 512x512 app/icons/512x512.png
magick icon.png -resize 256x256 app/icons/256x256.png
magick icon.png -resize 128x128 app/icons/128x128.png
magick icon.png -resize 64x64 app/icons/64x64.png
magick icon.png -resize 48x48 app/icons/48x48.png
magick icon.png -resize 32x32 app/icons/32x32.png
magick icon.png -resize 16x16 app/icons/16x16.png
magick icon.png -resize 192x192 packages/web/public/logo192.png
magick icon.png -resize 512x512 packages/web/public/logo512.png
magick icon.png -define icon:auto-resize="256,128,96,64,48,32,16" packages/web/public/favicon.ico

View File

@@ -5,6 +5,7 @@ services:
dbgate:
build: docker
# image: dbgate/dbgate:beta-alpine
# image: dbgate/dbgate:alpine
# image: dbgate/dbgate:beta
restart: always
ports:
@@ -15,8 +16,8 @@ services:
volumes:
- dbgate-data:/root/dbgate-data
environment:
WEB_ROOT: /dbgate
# environment:
# WEB_ROOT: /dbgate
# CONNECTIONS: mssql
# LABEL_mssql: MS Sql
@@ -25,11 +26,11 @@ services:
# PORT_mssql: 1433
# PASSWORD_mssql: Pwd2020Db
# ENGINE_mssql: mssql@dbgate-plugin-mssql
proxy:
# image: nginx
build: test/nginx
ports:
- 8082:80
# proxy:
# # image: nginx
# build: test/nginx
# ports:
# - 8082:80
# volumes:
# - /home/jena/test/chinook:/mnt/sqt

View File

@@ -1,10 +1,18 @@
FROM node:14
RUN apt-get update && apt-get install -y \
iputils-ping \
iproute2 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /home/dbgate-docker
COPY . .
RUN ["chmod", "+x", "/home/dbgate-docker/entrypoint.sh"]
WORKDIR /home/dbgate-docker
EXPOSE 3000
VOLUME /root/dbgate-data
CMD node bundle.js
CMD ["/home/dbgate-docker/entrypoint.sh"]

View File

@@ -2,9 +2,16 @@ FROM node:14-alpine
WORKDIR /home/dbgate-docker
RUN apk --no-cache upgrade \
&& apk --no-cache add \
iputils
COPY . .
RUN ["chmod", "+x", "/home/dbgate-docker/entrypoint.sh"]
WORKDIR /home/dbgate-docker
EXPOSE 3000
VOLUME /root/dbgate-data
CMD node bundle.js
CMD ["/home/dbgate-docker/entrypoint.sh"]

11
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
HOST_DOMAIN="dockerhost"
ping -q -c1 $HOST_DOMAIN > /dev/null 2>&1
if [ $? != 0 ]
then
HOST_IP=$(ip route | awk 'NR==1 {print $3}')
echo "$HOST_IP $HOST_DOMAIN" >> /etc/hosts
fi
node bundle.js

View File

@@ -6,8 +6,6 @@ function load() {
for (const packageName of fs.readdirSync('plugins')) {
if (!packageName.startsWith('dbgate-plugin-')) continue;
// TODO skip redis when creating output bundle
if (packageName == 'dbgate-plugin-redis') continue;
const dir = path.join('plugins', packageName);
const frontend = fs.readFileSync(path.join(dir, 'dist', 'frontend.js'), 'utf-8');
const readme = fs.readFileSync(path.join(dir, 'README.md'), 'utf-8');

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -1,6 +1,6 @@
{
"private": true,
"version": "4.7.4-beta.11",
"version": "4.8.8-beta.3",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -17,13 +17,11 @@
"start:tools": "yarn workspace dbgate-tools start",
"start:datalib": "yarn workspace dbgate-datalib start",
"start:filterparser": "yarn workspace dbgate-filterparser start",
"start:querysplitter": "yarn workspace dbgate-query-splitter start",
"build:sqltree": "yarn workspace dbgate-sqltree build",
"build:datalib": "yarn workspace dbgate-datalib build",
"build:filterparser": "yarn workspace dbgate-filterparser build",
"build:querysplitter": "yarn workspace dbgate-query-splitter build",
"build:tools": "yarn workspace dbgate-tools build",
"build:lib": "yarn build:querysplitter && yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib",
"build:lib": "yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib",
"build:app": "yarn plugins:copydist && cd app && yarn install && yarn build",
"build:api": "yarn workspace dbgate-api build",
"build:web:docker": "yarn workspace dbgate-web build",
@@ -43,11 +41,12 @@
"install:sqlite:docker": "cd docker && yarn init --yes && yarn add better-sqlite3 && cd ..",
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build && yarn install:sqlite:docker",
"start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"",
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn start:querysplitter\" \"yarn build:plugins:frontend:watch\"",
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn build:plugins:frontend:watch\"",
"ts:api": "yarn workspace dbgate-api ts",
"ts:web": "yarn workspace dbgate-web ts",
"ts": "yarn ts:api && yarn ts:web",
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn fillNativeModules && yarn build:plugins:frontend"
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn fillNativeModules && yarn build:plugins:frontend",
"dbgate-serve": "node packages/dbgate/bin/dbgate-serve.js"
},
"dependencies": {
"concurrently": "^5.1.0",

View File

@@ -4,11 +4,12 @@ DEVMODE=1
# HIDE_APP_EDITOR=1
DEVWEB=1
LOGINS=admin,test
# DEVWEB=1
# LOGINS=admin,test
LOGIN_PASSWORD_admin=admin
LOGIN_PERMISSIONS_admin=*
# LOGIN_PASSWORD_admin=admin
# LOGIN_PERMISSIONS_admin=*
LOGIN_PASSWORD_test=test
LOGIN_PERMISSIONS_test=~*, widgets/database
# LOGIN_PASSWORD_test=test
# LOGIN_PERMISSIONS_test=~*, widgets/database
# WORKSPACE_DIR=/home/jena/dbgate-data-2

View File

@@ -25,7 +25,7 @@
"compare-versions": "^3.6.0",
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-query-splitter": "^4.1.1",
"dbgate-query-splitter": "^4.9.0",
"dbgate-sqltree": "^4.1.1",
"dbgate-tools": "^4.1.1",
"diff": "^5.0.0",

View File

@@ -37,6 +37,7 @@ module.exports = {
// hideAppEditor: !!process.env.HIDE_APP_EDITOR,
allowShellConnection: platformInfo.allowShellConnection,
allowShellScripting: platformInfo.allowShellConnection,
isDocker: platformInfo.isDocker,
permissions,
login,
...currentVersion,

View File

@@ -176,8 +176,13 @@ module.exports = {
},
loadKeys_meta: true,
async loadKeys({ conid, database, root }) {
return this.loadDataCore('loadKeys', { conid, database, root });
async loadKeys({ conid, database, root, filter }) {
return this.loadDataCore('loadKeys', { conid, database, root, filter });
},
exportKeys_meta: true,
async exportKeys({ conid, database, options }) {
return this.loadDataCore('exportKeys', { conid, database, options });
},
loadKeyInfo_meta: true,

View File

@@ -62,6 +62,15 @@ module.exports = {
return true;
},
refresh_meta: true,
async refresh({ folders }, req) {
for (const folder of folders) {
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`all-files-changed`);
}
return true;
},
copy_meta: true,
async copy({ folder, file, newFile }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
@@ -177,4 +186,24 @@ module.exports = {
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeClassName));
return true;
},
getFileRealPath_meta: true,
async getFileRealPath({ folder, file }, req) {
if (folder.startsWith('archive:')) {
if (!hasPermission(`archive/write`, req)) return false;
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
return path.join(dir, file);
} else if (folder.startsWith('app:')) {
if (!hasPermission(`apps/write`, req)) return false;
const app = folder.substring('app:'.length);
return path.join(appdir(), app, file);
} else {
if (!hasPermission(`files/${folder}/write`, req)) return false;
const dir = path.join(filesdir(), folder);
if (!(await fs.exists(dir))) {
await fs.mkdir(dir);
}
return path.join(dir, file);
}
},
};

View File

@@ -1,3 +1,4 @@
const { filterName } = require('dbgate-tools');
const fs = require('fs');
const lineReader = require('line-reader');
const _ = require('lodash');
@@ -148,6 +149,19 @@ module.exports = {
return {};
},
loadFieldValues_meta: true,
async loadFieldValues({ jslid, field, search }) {
const datastore = await this.ensureDatastore(jslid);
const res = new Set();
await datastore.enumRows(row => {
if (!filterName(search, row[field])) return true;
res.add(row[field]);
return res.size < 100;
});
// @ts-ignore
return [...res].map(value => ({ value }));
},
async notifyChangedStats(stats) {
// console.log('SENDING STATS', JSON.stringify(stats));
const datastore = this.datastores[stats.jslid];

View File

@@ -125,6 +125,7 @@ module.exports = {
socket.emitChanged(`installed-plugins-changed`);
// this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
// await this.saveRemovePlugins();
return true;
},
uninstall_meta: true,
@@ -134,7 +135,8 @@ module.exports = {
await fs.rmdir(dir, { recursive: true });
socket.emitChanged(`installed-plugins-changed`);
// this.removedPlugins.push(packageName);
await this.saveRemovePlugins();
// await this.saveRemovePlugins();
return true;
},
upgrade_meta: true,
@@ -148,6 +150,7 @@ module.exports = {
}
socket.emitChanged(`installed-plugins-changed`);
return true;
},
command_meta: true,

View File

@@ -104,6 +104,7 @@ module.exports = {
scriptFile,
[
'--checkParent', // ...process.argv.slice(3)
'--is-forked-api',
...processArgs.getPassArgs(),
],
{
@@ -111,7 +112,7 @@ module.exports = {
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
env: {
...process.env,
DBGATE_API: global['API_PACKAGE'] || global['dbgateApiModulePath'] || process.argv[1],
DBGATE_API: global['API_PACKAGE'] || process.argv[1],
..._.fromPairs(pluginNames.map(name => [`PLUGIN_${_.camelCase(name)}`, getPluginBackendPath(name)])),
},
}

View File

@@ -142,6 +142,7 @@ module.exports = {
createDatabase_meta: true,
async createDatabase({ conid, name }) {
const opened = await this.ensureOpened(conid);
if (opened.connection.isReadOnly) return false;
opened.subprocess.send({ msgtype: 'createDatabase', name });
return { status: 'ok' };
},

View File

@@ -8,7 +8,7 @@ if (processArgs.startProcess) {
const proc = require('./proc');
const module = proc[processArgs.startProcess];
module.start();
} else if (!processArgs.checkParent && !global['API_PACKAGE'] && !global['dbgateApiModulePath']) {
} else if (!processArgs.checkParent && !global['API_PACKAGE']) {
const main = require('./main');
main.start();

View File

@@ -86,6 +86,10 @@ function start() {
if (platformInfo.isDocker) {
// server static files inside docker container
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
const port = process.env.PORT || 3000;
console.log('DbGate API listening on port (docker build)', port);
server.listen(port);
} else if (platformInfo.isNpmDist) {
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
getPort({
@@ -95,7 +99,7 @@ function start() {
),
}).then(port => {
server.listen(port, () => {
console.log(`DbGate API listening on port ${port}`);
console.log(`DbGate API listening on port ${port} (NPM build)`);
});
});
} else if (process.env.DEVWEB) {
@@ -104,7 +108,7 @@ function start() {
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../web/public')));
const port = process.env.PORT || 3000;
console.log('DbGate API & web listening on port', port);
console.log('DbGate API & web listening on port (dev web build)', port);
server.listen(port);
} else {
app.get(getExpressPath('/'), (req, res) => {
@@ -112,7 +116,7 @@ function start() {
});
const port = process.env.PORT || 3000;
console.log('DbGate API listening on port', port);
console.log('DbGate API listening on port (dev API build)', port);
server.listen(port);
}
@@ -151,8 +155,8 @@ function useAllControllers(app, electron) {
useController(app, electron, '/apps', apps);
}
function initializeElectronSender(electronSender) {
function setElectronSender(electronSender) {
socket.setElectronSender(electronSender);
}
module.exports = { start, useAllControllers, initializeElectronSender, configController: config };
module.exports = { start, useAllControllers, setElectronSender, configController: config };

View File

@@ -197,8 +197,12 @@ async function handleCollectionData({ msgid, options }) {
return handleDriverDataCore(msgid, driver => driver.readCollection(systemConnection, options));
}
async function handleLoadKeys({ msgid, root }) {
return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root));
async function handleLoadKeys({ msgid, root, filter }) {
return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root, filter));
}
async function handleExportKeys({ msgid, options }) {
return handleDriverDataCore(msgid, driver => driver.exportKeys(systemConnection, options));
}
async function handleLoadKeyInfo({ msgid, key }) {
@@ -207,6 +211,10 @@ async function handleLoadKeyInfo({ msgid, key }) {
async function handleCallMethod({ msgid, method, args }) {
return handleDriverDataCore(msgid, driver => {
if (storedConnection.isReadOnly) {
throw new Error('Connection is read only, cannot call custom methods');
}
ensureExecuteCustomScript(driver);
return driver.callMethod(systemConnection, method, args);
});
@@ -307,6 +315,7 @@ const messageHandlers = {
generateDeploySql: handleGenerateDeploySql,
loadFieldValues: handleLoadFieldValues,
sqlSelect: handleSqlSelect,
exportKeys: handleExportKeys,
// runCommand: handleRunCommand,
};

View File

@@ -3,6 +3,7 @@ const databaseConnectionProcess = require('./databaseConnectionProcess');
const serverConnectionProcess = require('./serverConnectionProcess');
const sessionProcess = require('./sessionProcess');
const jslDatastoreProcess = require('./jslDatastoreProcess');
const sshForwardProcess = require('./sshForwardProcess');
module.exports = {
connectProcess,
@@ -10,4 +11,5 @@ module.exports = {
serverConnectionProcess,
sessionProcess,
jslDatastoreProcess,
sshForwardProcess,
};

View File

@@ -0,0 +1,68 @@
const fs = require('fs-extra');
const platformInfo = require('../utility/platformInfo');
const childProcessChecker = require('../utility/childProcessChecker');
const { SSHConnection } = require('node-ssh-forward');
const { handleProcessCommunication } = require('../utility/processComm');
async function getSshConnection(connection) {
const sshConfig = {
endHost: connection.sshHost || '',
endPort: connection.sshPort || 22,
bastionHost: connection.sshBastionHost || '',
agentForward: connection.sshMode == 'agent',
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
username: connection.sshLogin,
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
privateKey:
connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
skipAutoPrivateKey: true,
noReadline: true,
};
const sshConn = new SSHConnection(sshConfig);
return sshConn;
}
async function handleStart({ connection, tunnelConfig }) {
try {
const sshConn = await getSshConnection(connection);
await sshConn.forward(tunnelConfig);
process.send({
msgtype: 'connected',
connection,
tunnelConfig,
});
} catch (err) {
process.send({
msgtype: 'error',
connection,
tunnelConfig,
errorMessage: err.message,
});
}
}
const messageHandlers = {
connect: handleStart,
};
async function handleMessage({ msgtype, ...other }) {
const handler = messageHandlers[msgtype];
await handler(other);
}
function start() {
childProcessChecker();
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
try {
await handleMessage(message);
} catch (e) {
console.error('sshForwardProcess - unhandled error', e);
}
});
}
module.exports = { start };

View File

@@ -0,0 +1,38 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
function doDump(dumper) {
return new Promise((resolve, reject) => {
dumper.once('end', () => {
resolve(true);
});
dumper.once('error', err => {
reject(err);
});
dumper.run();
});
}
async function dumpDatabase({
connection = undefined,
systemConnection = undefined,
driver = undefined,
outputFile,
databaseName,
schemaName,
}) {
console.log(`Dumping database`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
console.log(`Connected.`);
const dumper = await driver.createBackupDumper(pool, {
outputFile,
databaseName,
schemaName,
});
await doDump(dumper);
}
module.exports = dumpDatabase;

View File

@@ -0,0 +1,57 @@
const fs = require('fs');
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
const download = require('./download');
const stream = require('stream');
class ImportStream extends stream.Transform {
constructor(pool, driver) {
super({ objectMode: true });
this.pool = pool;
this.driver = driver;
}
async _transform(chunk, encoding, cb) {
try {
await this.driver.script(this.pool, chunk);
} catch (err) {
this.emit('error', err.message);
}
cb();
}
_flush(cb) {
this.push('finish');
cb();
this.emit('end');
}
}
function awaitStreamEnd(stream) {
return new Promise((resolve, reject) => {
stream.once('end', () => {
resolve(true);
});
stream.once('error', err => {
reject(err);
});
});
}
async function importDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, inputFile }) {
console.log(`Importing database`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
console.log(`Connected.`);
const downloadedFile = await download(inputFile);
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions());
const importStream = new ImportStream(pool, driver);
// @ts-ignore
splittedStream.pipe(importStream);
await awaitStreamEnd(importStream);
}
module.exports = importDatabase;

View File

@@ -21,6 +21,8 @@ const executeQuery = require('./executeQuery');
const loadFile = require('./loadFile');
const deployDb = require('./deployDb');
const initializeApiEnvironment = require('./initializeApiEnvironment');
const dumpDatabase = require('./dumpDatabase');
const importDatabase = require('./importDatabase');
const dbgateApi = {
queryReader,
@@ -45,6 +47,8 @@ const dbgateApi = {
loadFile,
deployDb,
initializeApiEnvironment,
dumpDatabase,
importDatabase,
};
requirePlugin.initializeDbgateApi(dbgateApi);

View File

@@ -159,10 +159,23 @@ class JsonLinesDatastore {
}
}
async enumRows(eachRow) {
await lock.acquire('reader', async () => {
await this._ensureReader(0, null);
for (;;) {
const line = await this._readLine(true);
if (line == null) break;
const shouldContinue = eachRow(line);
if (!shouldContinue) break;
}
});
}
async getRows(offset, limit, filter) {
const res = [];
await lock.acquire('reader', async () => {
await this._ensureReader(offset, filter);
// console.log(JSON.stringify(this.currentFilter, undefined, 2));
for (let i = 0; i < limit; i += 1) {
const line = await this._readLine(true);
if (line == null) break;

View File

@@ -39,7 +39,7 @@ async function loadConnection(driver, storedConnection, connectionMode) {
return storedConnection;
}
async function connectUtility(driver, storedConnection, connectionMode) {
async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
const connection = {
@@ -93,7 +93,7 @@ async function connectUtility(driver, storedConnection, connectionMode) {
}
}
const conn = await driver.connect(connection);
const conn = await driver.connect({ ...connection, ...additionalOptions });
return conn;
}

View File

@@ -3,11 +3,12 @@ const path = require('path');
const fs = require('fs');
const cleanDirectory = require('./cleanDirectory');
const platformInfo = require('./platformInfo');
const processArgs = require('./processArgs');
const createDirectories = {};
const ensureDirectory = (dir, clean) => {
if (!createDirectories[dir]) {
if (clean && fs.existsSync(dir)) {
if (clean && fs.existsSync(dir) && !platformInfo.isForkedApi) {
console.log(`Cleaning directory ${dir}`);
cleanDirectory(dir);
}
@@ -19,8 +20,18 @@ const ensureDirectory = (dir, clean) => {
}
};
function datadirCore() {
if (process.env.WORKSPACE_DIR) {
return process.env.WORKSPACE_DIR;
}
if (processArgs.workspaceDir) {
return processArgs.workspaceDir;
}
return path.join(os.homedir(), 'dbgate-data');
}
function datadir() {
const dir = path.join(os.homedir(), 'dbgate-data');
const dir = datadirCore();
ensureDirectory(dir);
return dir;
@@ -54,7 +65,10 @@ function packagedPluginsDir() {
}
if (platformInfo.isNpmDist) {
// node_modules
return global['dbgateApiPackagedPluginsPath'];
return global['PLUGINS_DIR'];
}
if (processArgs.pluginsDir) {
return processArgs.pluginsDir;
}
if (platformInfo.isElectronBundle) {
return path.resolve(__dirname, '../../plugins');

View File

@@ -6,6 +6,10 @@ function getJslFileName(jslid) {
if (archiveMatch) {
return path.join(resolveArchiveFolder(archiveMatch[1]), `${archiveMatch[2]}.jsonl`);
}
const fileMatch = jslid.match(/^file:\/\/(.*)$/);
if (fileMatch) {
return fileMatch[1];
}
return path.join(jsldir(), `${jslid}.jsonl`);
}

View File

@@ -10,7 +10,7 @@ const isMac = platform === 'darwin';
const isLinux = platform === 'linux';
const isDocker = fs.existsSync('/home/dbgate-docker/public');
const isDevMode = process.env.DEVMODE == '1';
const isNpmDist = !!global['dbgateApiModulePath'];
const isNpmDist = !!global['IS_NPM_DIST'];
const isForkedApi = processArgs.isForkedApi;
// function moduleAvailable(name) {

View File

@@ -9,10 +9,18 @@ function getNamedArg(name) {
const checkParent = process.argv.includes('--checkParent');
const startProcess = getNamedArg('--start-process');
const isForkedApi = process.argv.includes('--is-forked-api');
const pluginsDir = getNamedArg('--plugins-dir');
const workspaceDir = getNamedArg('--workspace-dir');
function getPassArgs() {
if (global['NATIVE_MODULES']) return ['--native-modules', global['NATIVE_MODULES']];
return [];
const res = [];
if (global['NATIVE_MODULES']) {
res.push('--native-modules', global['NATIVE_MODULES']);
}
if (global['PLUGINS_DIR']) {
res.push('--plugins-dir', global['PLUGINS_DIR']);
}
return res;
}
module.exports = {
@@ -20,4 +28,6 @@ module.exports = {
startProcess,
isForkedApi,
getPassArgs,
pluginsDir,
workspaceDir,
};

View File

@@ -1,6 +1,6 @@
let sseResponse = null;
let electronSender = null;
let init = '';
let init = [];
module.exports = {
setSseResponse(value) {
@@ -12,15 +12,25 @@ module.exports = {
},
emit(message, data) {
if (electronSender) {
if (init.length > 0) {
for (const item of init) {
electronSender.send(item.message, item.data == null ? null : item.data);
}
init = [];
}
electronSender.send(message, data == null ? null : data);
} else if (sseResponse) {
if (init) {
sseResponse.write(init);
init = '';
if (init.length > 0) {
for (const item of init) {
sseResponse.write(
`event: ${item.message}\ndata: ${JSON.stringify(item.data == null ? null : item.data)}\n\n`
);
}
init = [];
}
sseResponse.write(`event: ${message}\ndata: ${JSON.stringify(data == null ? null : data)}\n\n`);
} else {
init += sseResponse;
init.push([{ message, data }]);
}
},
emitChanged(key) {

View File

@@ -1,13 +1,11 @@
const { SSHConnection } = require('node-ssh-forward');
const fs = require('fs-extra');
const portfinder = require('portfinder');
const stableStringify = require('json-stable-stringify');
const _ = require('lodash');
const platformInfo = require('./platformInfo');
const AsyncLock = require('async-lock');
const lock = new AsyncLock();
const { fork } = require('child_process');
const processArgs = require('../utility/processArgs');
const sshConnectionCache = {};
const sshTunnelCache = {};
const CONNECTION_FIELDS = [
@@ -22,37 +20,42 @@ const CONNECTION_FIELDS = [
];
const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
async function getSshConnection(connection) {
const connectionCacheKey = stableStringify(_.pick(connection, CONNECTION_FIELDS));
if (sshConnectionCache[connectionCacheKey]) return sshConnectionCache[connectionCacheKey];
function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
let subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'sshForwardProcess',
...processArgs.getPassArgs(),
]);
const sshConfig = {
endHost: connection.sshHost || '',
endPort: connection.sshPort || 22,
bastionHost: connection.sshBastionHost || '',
agentForward: connection.sshMode == 'agent',
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
username: connection.sshLogin,
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
privateKey:
connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
skipAutoPrivateKey: true,
noReadline: true,
};
const sshConn = new SSHConnection(sshConfig);
sshConnectionCache[connectionCacheKey] = sshConn;
return sshConn;
subprocess.send({
msgtype: 'connect',
connection,
tunnelConfig,
});
return new Promise((resolve, reject) => {
subprocess.on('message', resp => {
// @ts-ignore
const { msgtype, errorMessage } = resp;
if (msgtype == 'connected') {
resolve(subprocess);
}
if (msgtype == 'error') {
reject(errorMessage);
}
});
subprocess.on('exit', code => {
console.log('SSH forward process exited');
delete sshTunnelCache[tunnelCacheKey];
});
});
}
async function getSshTunnel(connection) {
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
return await lock.acquire(tunnelCacheKey, async () => {
const sshConn = await getSshConnection(connection);
if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
// workaround for `getPortPromise` not releasing the port quickly enough
await new Promise(resolve => setTimeout(resolve, 500));
@@ -66,7 +69,8 @@ async function getSshTunnel(connection) {
`Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
);
const tunnel = await sshConn.forward(tunnelConfig);
const subprocess = await callForwardProcess(connection, tunnelConfig, tunnelCacheKey);
console.log(
`Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
);
@@ -74,6 +78,7 @@ async function getSshTunnel(connection) {
sshTunnelCache[tunnelCacheKey] = {
state: 'ok',
localPort,
subprocess,
};
return sshTunnelCache[tunnelCacheKey];
} catch (err) {

View File

@@ -31,9 +31,14 @@ module.exports = function useController(app, electron, route, controller) {
const handler = `${route.substring(1)}-${_.kebabCase(key)}`;
// console.log('REGISTERING HANDLER', handler);
electron.ipcMain.handle(handler, async (event, args) => {
const data = await controller[key](args);
// console.log('HANDLED API', handler, data);
return data;
try {
const data = await controller[key](args);
// console.log('HANDLED API', handler, data);
if (data === undefined) return null;
return data;
} catch (err) {
return { apiErrorMessage: err.message };
}
});
}
@@ -68,7 +73,7 @@ module.exports = function useController(app, electron, route, controller) {
res.json(data);
} catch (e) {
console.log(e);
res.status(500).json({ error: e.message });
res.status(500).json({ apiErrorMessage: e.message });
}
});
}

View File

@@ -652,7 +652,8 @@ export abstract class GridDisplay {
for (const name in filters) {
const column = this.isDynamicStructure ? null : this.columns.find(x => x.columnName == name);
if (!this.isDynamicStructure && !column) continue;
const filterType = this.isDynamicStructure ? this.filterTypeOverride ?? 'mongo' : getFilterType(column.dataType);
const filterType =
this.filterTypeOverride ?? (this.isDynamicStructure ? 'mongo' : getFilterType(column.dataType));
try {
const condition = parseFilter(filters[name], filterType);
const replaced = _.cloneDeepWith(condition, (expr: Expression) => {

View File

@@ -21,7 +21,7 @@ export class JslGridDisplay extends GridDisplay {
this.filterable = true;
this.supportsReload = supportsReload;
this.isDynamicStructure = isDynamicStructure;
if (isDynamicStructure) this.filterTypeOverride = 'string';
this.filterTypeOverride = 'eval';
if (structure?.columns) {
this.columns = _.uniqBy(

View File

@@ -1,27 +1,2 @@
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![NPM version](https://img.shields.io/npm/v/dbgate.svg)](https://www.npmjs.com/package/dbgate)
# DbGate - database administration tool
DbGate is fast and easy to use database administration tool for MySQL, PostgreSQL, SQL Server.
## Install using npm
```sh
npm install -g dbgate
```
After installing, you can run dbgate with command:
```sh
dbgate-serve
```
Then open http://localhost:5000 in your browser
## Download electron app
You can also download binary packages from https://dbgate.org . Or run from source code, as described on [github](https://github.com/dbgate/dbgate)
## Other dbgate packages
You can use some functionality of dbgate from your JavaScript code. See [dbgate-api](https://npmjs.com/dbgate-api) package.
## Screenshot
![Screenshot](https://raw.githubusercontent.com/dbgate/dbgate/master/screenshot.png)
This package is obsolete, please use [dbgate-serve](https://www.npmjs.com/package/dbgate-serve) package instead

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env node
const path = require('path');
require('dotenv').config();
global.dbgateApiModulePath = path.dirname(path.dirname(require.resolve('dbgate-api')));
global.dbgateApiPackagedPluginsPath = path.dirname(global.dbgateApiModulePath);
const dbgateApi = require('dbgate-api');
dbgateApi.getMainModule().start();

View File

@@ -9,24 +9,9 @@
"description": "Opensource database administration tool - web interface",
"author": "Jan Prochazka",
"license": "MIT",
"bin": {
"dbgate-serve": "./bin/dbgate-serve.js"
},
"keywords": [
"sql",
"dbgate",
"web"
],
"dependencies": {
"dbgate-api": "^4.1.1",
"dbgate-plugin-csv": "^4.1.1",
"dbgate-plugin-excel": "^4.1.1",
"dbgate-plugin-mongo": "^4.1.1",
"dbgate-plugin-mssql": "^4.1.1",
"dbgate-plugin-mysql": "^4.1.1",
"dbgate-plugin-postgres": "^4.1.1",
"dbgate-plugin-xml": "^4.1.1",
"dbgate-web": "^4.1.1",
"dotenv": "^16.0.0"
}
]
}

View File

@@ -254,10 +254,10 @@ const createParser = (filterType: FilterType) => {
eq: r => word('=').then(r.value).map(binaryCondition('=')),
ne: r => word('!=').then(r.value).map(binaryCondition('<>')),
ne2: r => word('<>').then(r.value).map(binaryCondition('<>')),
lt: r => word('<').then(r.value).map(binaryCondition('<')),
gt: r => word('>').then(r.value).map(binaryCondition('>')),
le: r => word('<=').then(r.value).map(binaryCondition('<=')),
ge: r => word('>=').then(r.value).map(binaryCondition('>=')),
lt: r => word('<').then(r.value).map(binaryCondition('<')),
gt: r => word('>').then(r.value).map(binaryCondition('>')),
startsWith: r => word('^').then(r.value).map(likeCondition('like', '#VALUE#%')),
endsWith: r => word('$').then(r.value).map(likeCondition('like', '%#VALUE#')),
contains: r => word('+').then(r.value).map(likeCondition('like', '%#VALUE#%')),
@@ -271,24 +271,30 @@ const createParser = (filterType: FilterType) => {
};
const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString'];
if (filterType == 'string') allowedValues.push('string1', 'string2', 'noQuotedString');
if (filterType == 'number') allowedValues.push('string1Num', 'string2Num', 'number');
if (filterType == 'string' || filterType == 'eval') {
allowedValues.push('string1', 'string2', 'noQuotedString');
}
if (filterType == 'number') {
allowedValues.push('string1Num', 'string2Num', 'number');
}
const allowedElements = ['null', 'notNull', 'eq', 'ne', 'ne2'];
if (filterType == 'number' || filterType == 'datetime') allowedElements.push('lt', 'gt', 'le', 'ge');
if (filterType == 'string')
allowedElements.push(
'empty',
'notEmpty',
'startsWith',
'endsWith',
'contains',
'startsWithNot',
'endsWithNot',
'containsNot'
);
if (filterType == 'logical') allowedElements.push('true', 'false', 'trueNum', 'falseNum');
if (filterType == 'datetime')
if (filterType == 'number' || filterType == 'datetime' || filterType == 'eval') {
allowedElements.push('le', 'ge', 'lt', 'gt');
}
if (filterType == 'string') {
allowedElements.push('empty', 'notEmpty');
}
if (filterType == 'eval' || filterType == 'string') {
allowedElements.push('startsWith', 'endsWith', 'contains', 'startsWithNot', 'endsWithNot', 'containsNot');
}
if (filterType == 'logical') {
allowedElements.push('true', 'false', 'trueNum', 'falseNum');
}
if (filterType == 'eval') {
allowedElements.push('true', 'false');
}
if (filterType == 'datetime') {
allowedElements.push(
'yearMonthDaySecond',
'yearMonthDayMinute',
@@ -308,10 +314,13 @@ const createParser = (filterType: FilterType) => {
'thisYear',
'nextYear'
);
}
// must be last
if (filterType == 'string') allowedElements.push('valueTestStr');
else allowedElements.push('valueTestEq');
if (filterType == 'string' || filterType == 'eval') {
allowedElements.push('valueTestStr');
} else {
allowedElements.push('valueTestEq');
}
return P.createLanguage(langDef);
};
@@ -321,6 +330,7 @@ const parsers = {
string: createParser('string'),
datetime: createParser('datetime'),
logical: createParser('logical'),
eval: createParser('eval'),
mongo: mongoParser,
};

View File

@@ -1,3 +1,3 @@
// import types from 'dbgate-types';
export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'mongo';
export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'eval' | 'mongo';

View File

@@ -1 +0,0 @@
lib

View File

@@ -1,53 +0,0 @@
[![NPM version](https://img.shields.io/npm/v/dbgate-query-splitter.svg)](https://www.npmjs.com/package/dbgate-query-splitter)
# dbgate-query-splitter
Splits long SQL query into into particular statements. Designed to have zero dependencies and to be fast. Also supports nodejs-streams.
Supports following SQL dialects:
- MySQL
- PostgreSQL
- SQLite
- Microsoft SQL Server
## Usage
```js
import { splitQuery, mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions } from 'dbgate-query-splitter';
const output = splitQuery('SELECT * FROM `table1`;SELECT * FROM `table2`;', mysqlSplitterOptions);
// output is ['SELECT * FROM `table1`', 'SELECT * FROM `table2`']
```
## Streaming support in nodejs
Function splitQueryStream accepts input stream and query options. Result is object stream, each object for one splitted query.
Tokens must not be divided into more input chunks. This can be accomplished eg. when input stream emits one chunk per line (eg. using byline module)
```js
const { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions } = require('dbgate-query-splitter');
const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
const fs = require('fs');
const byline = require('byline');
const fileStream = fs.createReadStream('INPUT_FILE_NAME', 'utf-8');
const lineStream = byline(fileStream);
const splittedStream = splitQueryStream(lineStream, mysqlSplitterOptions);
```
## Contributing
Please run tests before pushing any changes.
```sh
yarn test
```
## Supported syntax
- Comments
- Dollar strings (PostgreSQL)
- GO separators (MS SQL)
- Custom delimiter, setby DELIMITER keyword (MySQL)

View File

@@ -1,5 +0,0 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
};

View File

@@ -1,37 +0,0 @@
{
"version": "4.1.1",
"name": "dbgate-query-splitter",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"description": "SQL Query splitter for verious database engines",
"homepage": "https://github.com/dbgate/dbgate/tree/master/packages/query-splitter",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate"
},
"author": "Jan Prochazka",
"license": "MIT",
"keywords": [
"SQL",
"query",
"split",
"parse"
],
"scripts": {
"build": "tsc",
"start": "tsc --watch",
"test": "jest",
"test:ci": "jest --json --outputFile=result.json --testLocationInResults"
},
"files": [
"lib"
],
"devDependencies": {
"dbgate-types": "^4.1.1",
"@types/jest": "^25.1.4",
"@types/node": "^13.7.0",
"jest": "^24.9.0",
"ts-jest": "^25.2.1",
"typescript": "^4.4.3"
}
}

View File

@@ -1,2 +0,0 @@
export { splitQuery } from './splitQuery';
export * from './options';

View File

@@ -1,85 +0,0 @@
export interface SplitterOptions {
stringsBegins: string[];
stringsEnds: { [begin: string]: string };
stringEscapes: { [begin: string]: string };
allowSemicolon: boolean;
allowCustomDelimiter: boolean;
allowGoDelimiter: boolean;
allowDollarDollarString: boolean;
noSplit: boolean;
doubleDashComments: boolean;
multilineComments: boolean;
javaScriptComments: boolean;
returnRichInfo: boolean;
}
export const defaultSplitterOptions: SplitterOptions = {
stringsBegins: ["'"],
stringsEnds: { "'": "'" },
stringEscapes: { "'": "'" },
allowSemicolon: true,
allowCustomDelimiter: false,
allowGoDelimiter: false,
allowDollarDollarString: false,
noSplit: false,
doubleDashComments: true,
multilineComments: true,
javaScriptComments: false,
returnRichInfo: false,
};
export const mysqlSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
allowCustomDelimiter: true,
stringsBegins: ["'", '`'],
stringsEnds: { "'": "'", '`': '`' },
stringEscapes: { "'": '\\', '`': '`' },
};
export const mssqlSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
allowSemicolon: false,
allowGoDelimiter: true,
stringsBegins: ["'", '['],
stringsEnds: { "'": "'", '[': ']' },
stringEscapes: { "'": "'" },
};
export const postgreSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
allowDollarDollarString: true,
stringsBegins: ["'", '"'],
stringsEnds: { "'": "'", '"': '"' },
stringEscapes: { "'": "'", '"': '"' },
};
export const sqliteSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
stringsBegins: ["'", '"'],
stringsEnds: { "'": "'", '"': '"' },
stringEscapes: { "'": "'", '"': '"' },
};
export const mongoSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
stringsBegins: ["'", '"'],
stringsEnds: { "'": "'", '"': '"' },
stringEscapes: { "'": '\\', '"': '\\' },
};
export const noSplitSplitterOptions: SplitterOptions = {
...defaultSplitterOptions,
noSplit: true,
};

View File

@@ -1,408 +0,0 @@
import { SplitterOptions, defaultSplitterOptions } from './options';
const SEMICOLON = ';';
export interface SplitStreamContext {
options: SplitterOptions;
currentDelimiter: string;
pushOutput: (item: SplitResultItem) => void;
commandPart: string;
line: number;
column: number;
streamPosition: number;
commandStartPosition: number;
commandStartLine: number;
commandStartColumn: number;
}
export interface SplitLineContext extends SplitStreamContext {
source: string;
position: number;
// output: string[];
end: number;
wasDataOnLine: boolean;
currentCommandStart: number;
// unread: string;
// currentStatement: string;
// semicolonKeyTokenRegex: RegExp;
}
export interface SplitPositionDefinition {
position: number;
line: number;
column: number;
}
export interface SplitResultItemRich {
text: string;
start: SplitPositionDefinition;
end: SplitPositionDefinition;
trimStart?: SplitPositionDefinition;
trimEnd?: SplitPositionDefinition;
}
export type SplitResultItem = string | SplitResultItemRich;
function movePosition(context: SplitLineContext, count: number) {
if (context.options.returnRichInfo) {
let { source, position, line, column, streamPosition } = context;
while (count > 0) {
if (source[position] == '\n') {
line += 1;
column = 0;
} else {
column += 1;
}
position += 1;
streamPosition += 1;
count -= 1;
}
context.position = position;
context.streamPosition = streamPosition;
context.line = line;
context.column = column;
} else {
context.position += count;
}
}
function isStringEnd(s: string, pos: number, endch: string, escapech: string) {
if (!escapech) {
return s[pos] == endch;
}
if (endch == escapech) {
return s[pos] == endch && s[pos + 1] != endch;
} else {
return s[pos] == endch && s[pos - 1] != escapech;
}
}
interface Token {
type: 'string' | 'delimiter' | 'whitespace' | 'eoln' | 'data' | 'set_delimiter' | 'comment' | 'go_delimiter';
length: number;
value?: string;
}
const WHITESPACE_TOKEN: Token = {
type: 'whitespace',
length: 1,
};
const EOLN_TOKEN: Token = {
type: 'eoln',
length: 1,
};
const DATA_TOKEN: Token = {
type: 'data',
length: 1,
};
function scanDollarQuotedString(context: SplitLineContext): Token {
if (!context.options.allowDollarDollarString) return null;
let pos = context.position;
const s = context.source;
const match = /^(\$[a-zA-Z0-9_]*\$)/.exec(s.slice(pos));
if (!match) return null;
const label = match[1];
pos += label.length;
while (pos < context.end) {
if (s.slice(pos).startsWith(label)) {
return {
type: 'string',
length: pos + label.length - context.position,
};
}
pos++;
}
return null;
}
function scanToken(context: SplitLineContext): Token {
let pos = context.position;
const s = context.source;
const ch = s[pos];
if (context.options.stringsBegins.includes(ch)) {
pos++;
const endch = context.options.stringsEnds[ch];
const escapech = context.options.stringEscapes[ch];
while (pos < context.end && !isStringEnd(s, pos, endch, escapech)) {
if (endch == escapech && s[pos] == endch && s[pos + 1] == endch) {
pos += 2;
} else {
pos++;
}
}
return {
type: 'string',
length: pos - context.position + 1,
};
}
if (context.currentDelimiter && s.slice(pos).startsWith(context.currentDelimiter)) {
return {
type: 'delimiter',
length: context.currentDelimiter.length,
};
}
if (ch == ' ' || ch == '\t' || ch == '\r') {
return WHITESPACE_TOKEN;
}
if (ch == '\n') {
return EOLN_TOKEN;
}
if (context.options.doubleDashComments && ch == '-' && s[pos + 1] == '-') {
while (pos < context.end && s[pos] != '\n') pos++;
return {
type: 'comment',
length: pos - context.position,
};
}
if (context.options.multilineComments && ch == '/' && s[pos + 1] == '*') {
pos += 2;
while (pos < context.end) {
if (s[pos] == '*' && s[pos + 1] == '/') break;
pos++;
}
return {
type: 'comment',
length: pos - context.position + 2,
};
}
if (context.options.allowCustomDelimiter && !context.wasDataOnLine) {
const m = s.slice(pos).match(/^DELIMITER[ \t]+([^\n]+)/i);
if (m) {
return {
type: 'set_delimiter',
value: m[1].trim(),
length: m[0].length,
};
}
}
if (context.options.allowGoDelimiter && !context.wasDataOnLine) {
const m = s.slice(pos).match(/^GO[\t\r ]*(\n|$)/i);
if (m) {
return {
type: 'go_delimiter',
length: m[0].length - 1,
};
}
}
const dollarString = scanDollarQuotedString(context);
if (dollarString) return dollarString;
return DATA_TOKEN;
}
function pushQuery(context: SplitLineContext) {
const sql = (context.commandPart || '') + context.source.slice(context.currentCommandStart, context.position);
const trimmed = sql.trim();
if (trimmed) {
if (context.options.returnRichInfo) {
context.pushOutput(
countTrimmedPositions(sql, {
text: trimmed,
start: {
position: context.commandStartPosition,
line: context.commandStartLine,
column: context.commandStartColumn,
},
end: {
position: context.streamPosition,
line: context.line,
column: context.column,
},
})
);
} else {
context.pushOutput(trimmed);
}
}
}
function countTrimmedPositions(full: string, positions: SplitResultItemRich): SplitResultItemRich {
const startIndex = full.indexOf(positions.text);
const trimStart = { ...positions.start };
for (let i = 0; i < startIndex; i += 1) {
if (full[i] == '\n') {
trimStart.position += 1;
trimStart.line += 1;
trimStart.column = 0;
} else {
trimStart.position += 1;
trimStart.column += 1;
}
}
return {
...positions,
trimStart,
trimEnd: positions.end,
};
}
function markStartCommand(context: SplitLineContext) {
if (context.options.returnRichInfo) {
context.commandStartPosition = context.streamPosition;
context.commandStartLine = context.line;
context.commandStartColumn = context.column;
}
}
export function splitQueryLine(context: SplitLineContext) {
while (context.position < context.end) {
const token = scanToken(context);
if (!token) {
// nothing special, move forward
movePosition(context, 1);
continue;
}
switch (token.type) {
case 'string':
movePosition(context, token.length);
context.wasDataOnLine = true;
break;
case 'comment':
movePosition(context, token.length);
context.wasDataOnLine = true;
break;
case 'eoln':
movePosition(context, token.length);
context.wasDataOnLine = false;
break;
case 'data':
movePosition(context, token.length);
context.wasDataOnLine = true;
break;
case 'whitespace':
movePosition(context, token.length);
break;
case 'set_delimiter':
pushQuery(context);
context.commandPart = '';
context.currentDelimiter = token.value;
movePosition(context, token.length);
context.currentCommandStart = context.position;
markStartCommand(context);
break;
case 'go_delimiter':
pushQuery(context);
context.commandPart = '';
movePosition(context, token.length);
context.currentCommandStart = context.position;
markStartCommand(context);
break;
case 'delimiter':
pushQuery(context);
context.commandPart = '';
movePosition(context, token.length);
context.currentCommandStart = context.position;
markStartCommand(context);
break;
}
}
if (context.end > context.currentCommandStart) {
context.commandPart += context.source.slice(context.currentCommandStart, context.position);
}
}
export function getInitialDelimiter(options: SplitterOptions) {
return options?.allowSemicolon === false ? null : SEMICOLON;
}
export function finishSplitStream(context: SplitStreamContext) {
const trimmed = context.commandPart.trim();
if (trimmed) {
if (context.options.returnRichInfo) {
context.pushOutput(
countTrimmedPositions(context.commandPart, {
text: trimmed,
start: {
position: context.commandStartPosition,
line: context.commandStartLine,
column: context.commandStartColumn,
},
end: {
position: context.streamPosition,
line: context.line,
column: context.column,
},
})
);
} else {
context.pushOutput(trimmed);
}
}
}
export function splitQuery(sql: string, options: SplitterOptions = null): SplitResultItem[] {
const usedOptions = {
...defaultSplitterOptions,
...options,
};
if (usedOptions.noSplit) {
if (usedOptions.returnRichInfo) {
const lines = sql.split('\n');
return [
{
text: sql,
start: {
position: 0,
line: 0,
column: 0,
},
end: {
position: sql.length,
line: lines.length,
column: lines[lines.length - 1]?.length || 0,
},
},
];
}
return [sql];
}
const output = [];
const context: SplitLineContext = {
source: sql,
end: sql.length,
currentDelimiter: getInitialDelimiter(options),
position: 0,
column: 0,
line: 0,
currentCommandStart: 0,
commandStartLine: 0,
commandStartColumn: 0,
commandStartPosition: 0,
streamPosition: 0,
pushOutput: cmd => output.push(cmd),
wasDataOnLine: false,
options: usedOptions,
commandPart: '',
};
splitQueryLine(context);
finishSplitStream(context);
return output;
}

View File

@@ -1,52 +0,0 @@
import stream from 'stream';
import {
SplitStreamContext,
getInitialDelimiter,
SplitLineContext,
splitQueryLine,
finishSplitStream,
} from './splitQuery';
import { SplitterOptions } from './options';
export class SplitQueryStream extends stream.Transform {
context: SplitStreamContext;
constructor(options: SplitterOptions) {
super({ objectMode: true });
this.context = {
commandPart: '',
commandStartLine: 0,
commandStartColumn: 0,
commandStartPosition: 0,
streamPosition: 0,
line: 0,
column: 0,
options,
currentDelimiter: getInitialDelimiter(options),
pushOutput: cmd => this.push(cmd),
};
}
_transform(chunk, encoding, done) {
const lineContext: SplitLineContext = {
...this.context,
position: 0,
currentCommandStart: 0,
wasDataOnLine: false,
source: chunk,
end: chunk.length,
};
splitQueryLine(lineContext);
this.context.commandPart = lineContext.commandPart;
done();
}
_flush(done) {
finishSplitStream(this.context);
done();
}
}
export function splitQueryStream(sourceStream, options: SplitterOptions) {
const splitter = new SplitQueryStream(options);
sourceStream.pipe(splitter);
return splitter;
}

View File

@@ -1,173 +0,0 @@
import {
mysqlSplitterOptions,
mssqlSplitterOptions,
postgreSplitterOptions,
mongoSplitterOptions,
noSplitSplitterOptions,
} from './options';
import { splitQuery } from './splitQuery';
test('simple query', () => {
const output = splitQuery('select * from A');
expect(output).toEqual(['select * from A']);
});
test('correct split 2 queries', () => {
const output = splitQuery('SELECT * FROM `table1`;SELECT * FROM `table2`;', mysqlSplitterOptions);
expect(output).toEqual(['SELECT * FROM `table1`', 'SELECT * FROM `table2`']);
});
test('correct split 2 queries - no end semicolon', () => {
const output = splitQuery('SELECT * FROM `table1`;SELECT * FROM `table2`', mysqlSplitterOptions);
expect(output).toEqual(['SELECT * FROM `table1`', 'SELECT * FROM `table2`']);
});
test('delete empty query', () => {
const output = splitQuery(';;;\n;;SELECT * FROM `table1`;;;;;SELECT * FROM `table2`;;; ;;;', mysqlSplitterOptions);
expect(output).toEqual(['SELECT * FROM `table1`', 'SELECT * FROM `table2`']);
});
test('should handle double backtick', () => {
const input = ['CREATE TABLE `a``b` (`c"d` INT)', 'CREATE TABLE `a````b` (`c"d` INT)'];
const output = splitQuery(input.join(';\n') + ';', mysqlSplitterOptions);
expect(output).toEqual(input);
});
test('semicolon inside string', () => {
const input = ['CREATE TABLE a', "INSERT INTO a (x) VALUES ('1;2;3;4')"];
const output = splitQuery(input.join(';\n') + ';', mysqlSplitterOptions);
expect(output).toEqual(input);
});
test('semicolon inside identyifier - mssql', () => {
const input = ['CREATE TABLE [a;1]', "INSERT INTO [a;1] (x) VALUES ('1')"];
const output = splitQuery(input.join(';\n') + ';', {
...mssqlSplitterOptions,
allowSemicolon: true,
});
expect(output).toEqual(input);
});
test('delimiter test', () => {
const input = 'SELECT 1;\n DELIMITER $$\n SELECT 2; SELECT 3; \n DELIMITER ;';
const output = splitQuery(input, mysqlSplitterOptions);
expect(output).toEqual(['SELECT 1', 'SELECT 2; SELECT 3;']);
});
test('one line comment test', () => {
const input = 'SELECT 1 -- comment1;comment2\n;SELECT 2';
const output = splitQuery(input, mysqlSplitterOptions);
expect(output).toEqual(['SELECT 1 -- comment1;comment2', 'SELECT 2']);
});
test('multi line comment test', () => {
const input = 'SELECT 1 /* comment1;comment2\ncomment3*/;SELECT 2';
const output = splitQuery(input, mysqlSplitterOptions);
expect(output).toEqual(['SELECT 1 /* comment1;comment2\ncomment3*/', 'SELECT 2']);
});
test('dollar string', () => {
const input = 'CREATE PROC $$ SELECT 1; SELECT 2; $$ ; SELECT 3';
const output = splitQuery(input, postgreSplitterOptions);
expect(output).toEqual(['CREATE PROC $$ SELECT 1; SELECT 2; $$', 'SELECT 3']);
});
test('go delimiter', () => {
const input = 'SELECT 1\ngo\nSELECT 2';
const output = splitQuery(input, mssqlSplitterOptions);
expect(output).toEqual(['SELECT 1', 'SELECT 2']);
});
test('no split', () => {
const input = 'SELECT 1;SELECT 2';
const output = splitQuery(input, noSplitSplitterOptions);
expect(output).toEqual(['SELECT 1;SELECT 2']);
});
test('split mongo', () => {
const input = 'db.collection.insert({x:1});db.collection.insert({y:2})';
const output = splitQuery(input, mongoSplitterOptions);
expect(output).toEqual(['db.collection.insert({x:1})', 'db.collection.insert({y:2})']);
});
test('count lines', () => {
const output = splitQuery('SELECT * FROM `table1`;\nSELECT * FROM `table2`;', {
...mysqlSplitterOptions,
returnRichInfo: true,
});
expect(output).toEqual(
expect.arrayContaining([
expect.objectContaining({
text: 'SELECT * FROM `table1`',
trimStart: expect.objectContaining({
position: 0,
line: 0,
column: 0,
}),
end: expect.objectContaining({
position: 22,
line: 0,
column: 22,
}),
}),
expect.objectContaining({
text: 'SELECT * FROM `table2`',
trimStart: expect.objectContaining({
position: 24,
line: 1,
column: 0,
}),
end: expect.objectContaining({
position: 46,
line: 1,
column: 22,
}),
}),
])
);
});
test('count lines with flush', () => {
const output = splitQuery('SELECT * FROM `table1`;\nSELECT * FROM `table2`', {
...mysqlSplitterOptions,
returnRichInfo: true,
});
expect(output).toEqual(
expect.arrayContaining([
expect.objectContaining({
text: 'SELECT * FROM `table1`',
trimStart: expect.objectContaining({
position: 0,
line: 0,
column: 0,
}),
end: expect.objectContaining({
position: 22,
line: 0,
column: 22,
}),
}),
expect.objectContaining({
text: 'SELECT * FROM `table2`',
trimStart: expect.objectContaining({
position: 24,
line: 1,
column: 0,
}),
end: expect.objectContaining({
position: 46,
line: 1,
column: 22,
}),
}),
])
);
});

View File

@@ -1,40 +0,0 @@
import { mysqlSplitterOptions, mssqlSplitterOptions, postgreSplitterOptions, noSplitSplitterOptions } from './options';
import stream from 'stream';
import { splitQueryStream } from './splitQueryStream';
function createInputStream(...lines) {
const pass = new stream.PassThrough({
objectMode: true,
});
lines.forEach(line => pass.write(line));
pass.end();
return pass;
}
function streamToArray(streamSource) {
return new Promise((resolve, reject) => {
const res = [];
streamSource.on('data', x => res.push(x));
streamSource.on('end', () => resolve(res));
});
}
test('stream: simple query', async () => {
const output = await streamToArray(splitQueryStream(createInputStream('select * from A'), mysqlSplitterOptions));
expect(output).toEqual(['select * from A']);
});
test('stream: query on 2 lines', async () => {
const output = await streamToArray(splitQueryStream(createInputStream('select * ', 'from A'), mysqlSplitterOptions));
expect(output).toEqual(['select * from A']);
});
test('stream: query on 2 lines', async () => {
const output = await streamToArray(
splitQueryStream(
createInputStream('SELECT * ', 'FROM `table1`;', 'SELECT *', ' FROM `table2`'),
mysqlSplitterOptions
)
);
expect(output).toEqual(['SELECT * FROM `table1`', 'SELECT * FROM `table2`']);
});

View File

@@ -1,14 +0,0 @@
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"declaration": true,
"skipLibCheck": true,
"outDir": "lib",
"preserveWatchOutput": true,
"esModuleInterop": true
},
"include": [
"src/**/*"
]
}

27
packages/serve/README.md Normal file
View File

@@ -0,0 +1,27 @@
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![NPM version](https://img.shields.io/npm/v/dbgate.svg)](https://www.npmjs.com/package/dbgate)
# DbGate - database administration tool
DbGate is fast and easy to use database administration tool for MySQL, PostgreSQL, SQL Server.
## Install using npm
```sh
npm install -g dbgate-serve
```
After installing, you can run dbgate with command:
```sh
dbgate-serve
```
Then open http://localhost:5000 in your browser
## Download electron app
You can also download binary packages from https://dbgate.org . Or run from source code, as described on [github](https://github.com/dbgate/dbgate)
## Other dbgate packages
You can use some functionality of dbgate from your JavaScript code. See [dbgate-api](https://npmjs.com/dbgate-api) package.
## Screenshot
![Screenshot](https://raw.githubusercontent.com/dbgate/dbgate/master/screenshot.png)

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env node
const path = require('path');
require('dotenv').config();
global.API_PACKAGE = path.dirname(path.dirname(require.resolve('dbgate-api')));
global.PLUGINS_DIR = path.dirname(global.API_PACKAGE);
global.IS_NPM_DIST = true;
const dbgateApi = require('dbgate-api');
dbgateApi.getMainModule().start();

View File

@@ -0,0 +1,32 @@
{
"name": "dbgate-serve",
"version": "4.1.1",
"homepage": "https://dbgate.org/",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"description": "Opensource database administration tool - web interface",
"author": "Jan Prochazka",
"license": "MIT",
"bin": {
"dbgate-serve": "./bin/dbgate-serve.js"
},
"keywords": [
"sql",
"dbgate",
"web"
],
"dependencies": {
"dbgate-api": "^4.1.1",
"dbgate-plugin-csv": "^4.1.1",
"dbgate-plugin-excel": "^4.1.1",
"dbgate-plugin-mongo": "^4.1.1",
"dbgate-plugin-mssql": "^4.1.1",
"dbgate-plugin-mysql": "^4.1.1",
"dbgate-plugin-postgres": "^4.1.1",
"dbgate-plugin-xml": "^4.1.1",
"dbgate-web": "^4.1.1",
"dotenv": "^16.0.0"
}
}

View File

@@ -32,7 +32,7 @@
},
"dependencies": {
"lodash": "^4.17.21",
"dbgate-query-splitter": "^4.1.1",
"dbgate-query-splitter": "^4.9.0",
"dbgate-sqltree": "^4.1.1",
"uuid": "^3.4.0"
}

View File

@@ -49,6 +49,14 @@ export class ScriptWriter {
}
}
dumpDatabase(options) {
this._put(`await dbgateApi.dumpDatabase(${JSON.stringify(options)});`);
}
importDatabase(options) {
this._put(`await dbgateApi.importDatabase(${JSON.stringify(options)});`);
}
comment(s) {
this._put(`// ${s}`);
}
@@ -121,6 +129,20 @@ export class ScriptWriterJson {
});
}
dumpDatabase(options) {
this.commands.push({
type: 'dumpDatabase',
options,
});
}
importDatabase(options) {
this.commands.push({
type: 'importDatabase',
options,
});
}
getScript(schedule = null) {
return {
type: 'json',
@@ -158,6 +180,12 @@ export function jsonScriptToJavascript(json) {
case 'comment':
script.comment(cmd.text);
break;
case 'dumpDatabase':
script.dumpDatabase(cmd.options);
break;
case 'importDatabase':
script.importDatabase(cmd.options);
break;
}
}

View File

@@ -23,6 +23,7 @@ export const driverBase = {
dumperClass: SqlDumper,
dialect,
databaseEngineTypes: ['sql'],
supportedCreateDatabase: true,
async analyseFull(pool, version) {
const analyser = new this.analyserClass(pool, this, version);

View File

@@ -33,6 +33,27 @@ import _compact from 'lodash/compact';
// return DoMatch(Filter, value) || camelMatch;
// }
function fuzzysearch(needle, haystack) {
var hlen = haystack.length;
var nlen = needle.length;
if (nlen > hlen) {
return false;
}
if (nlen === hlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < nlen; i++) {
var nch = needle.charCodeAt(i);
while (j < hlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
export function filterName(filter: string, ...names: string[]) {
if (!filter) return true;
@@ -42,7 +63,7 @@ export function filterName(filter: string, ...names: string[]) {
const namesCompacted = _compact(names);
for (const token of tokens) {
const tokenUpper = token.toUpperCase();
const found = namesCompacted.find(name => name.toUpperCase().includes(tokenUpper));
const found = namesCompacted.find(name => fuzzysearch(tokenUpper, name.toUpperCase()));
if (!found) return false;
}

View File

@@ -42,15 +42,31 @@ export interface NewObjectTemplate {
sql: string;
}
export interface SupportedDbKeyType {
name: string;
label: string;
dbKeyFields: { name: string }[];
addMethod: string;
keyColumn?: string;
showItemList?: boolean;
}
export interface SqlBackupDumper {
run();
}
export interface EngineDriver {
engine: string;
title: string;
defaultPort?: number;
databaseEngineTypes: string[];
editorMode?: string;
readOnlySessions: boolean;
supportedKeyTypes: { name: string; label: string }[];
supportedKeyTypes: SupportedDbKeyType[];
supportsDatabaseUrl?: boolean;
supportsDatabaseDump?: boolean;
isElectronOnly?: boolean;
supportedCreateDatabase?: boolean;
showConnectionField?: (field: string, values: any) => boolean;
showConnectionTab?: (tab: 'ssl' | 'sshTunnel', values: any) => boolean;
beforeConnectionSave?: (values: any) => any;
@@ -78,7 +94,8 @@ export interface EngineDriver {
name: string;
}[]
>;
loadKeys(pool, root: string): Promise;
loadKeys(pool, root: string, filter?: string): Promise;
exportKeys(pool, options: {}): Promise;
loadKeyInfo(pool, key): Promise;
loadKeyTableRange(pool, key, cursor, count): Promise;
loadFieldValues(pool: any, name: NamedObjectInfo, field: string, search: string): Promise;
@@ -87,6 +104,7 @@ export interface EngineDriver {
dialect: SqlDialect;
dialectByVersion(version): SqlDialect;
createDumper(options = null): SqlDumper;
createBackupDumper(pool: any, options): Promise<SqlBackupDumper>;
getAuthTypes(): EngineAuthType[];
readCollection(pool: any, options: ReadCollectionOptions): Promise<any>;
updateCollection(pool: any, changeSet: any): Promise<any>;

View File

@@ -3,6 +3,7 @@ import { EngineDriver } from './engines';
export interface FileFormatDefinition {
storageType: string;
extension: string;
extensions?: string[];
name: string;
readerFunc?: string;
writerFunc?: string;

View File

@@ -24,7 +24,7 @@
"chartjs-adapter-moment": "^1.0.0",
"cross-env": "^7.0.3",
"dbgate-datalib": "^4.1.1",
"dbgate-query-splitter": "^4.1.1",
"dbgate-query-splitter": "^4.9.0",
"dbgate-sqltree": "^4.1.1",
"dbgate-tools": "^4.1.1",
"dbgate-types": "^4.1.1",
@@ -56,6 +56,7 @@
},
"dependencies": {
"chartjs-plugin-zoom": "^1.2.0",
"date-fns": "^2.28.0",
"interval-operations": "^1.0.7"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -17,8 +17,10 @@
import { apiCall } from './utility/api';
import { getConfig, getSettings, getUsedApps } from './utility/metadataLoaders';
import AppTitleProvider from './utility/AppTitleProvider.svelte';
import getElectron from './utility/getElectron';
let loadedApi = false;
let loadedPlugins = false;
async function loadApi() {
// if (shouldWaitForElectronInitialize()) {
@@ -61,6 +63,8 @@
$: {
if (loadedApi && $loadingPluginStore?.loaded) {
setAppLoaded();
loadedPlugins = true;
getElectron()?.send('app-started');
}
}
</script>
@@ -72,7 +76,7 @@
<CommandListener />
<PluginsProvider />
<AppTitleProvider />
{#if $loadingPluginStore?.loaded}
{#if loadedPlugins}
<OpenTabsOnStartup />
<Screen />
{:else}

View File

@@ -29,6 +29,7 @@
import { getDatabaseList, useUsedApps } from '../utility/metadataLoaders';
import { getLocalStorage } from '../utility/storageCache';
import { apiCall } from '../utility/api';
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
export let data;
export let passProps;
@@ -58,7 +59,14 @@
}
};
const handleSqlRestore = () => {
showModal(ImportDatabaseDumpModal, {
connection: data,
});
};
const getContextMenu = () => {
const driver = $extensions.drivers.find(x => x.engine == data.engine);
const config = getCurrentConfig();
const handleRefresh = () => {
apiCall('server-connections/refresh', { conid: data._id });
@@ -146,15 +154,19 @@
text: 'Disconnect',
onClick: handleDisconnect,
},
$openedConnections.includes(data._id) && {
text: 'Create database',
onClick: handleCreateDatabase,
},
$openedConnections.includes(data._id) &&
driver?.supportedCreateDatabase &&
!data.isReadOnly && {
text: 'Create database',
onClick: handleCreateDatabase,
},
],
data.singleDatabase && [
{ divider: true },
getDatabaseMenuItems(data, data.defaultDatabase, $extensions, $currentDatabase, $apps),
],
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlRestore, text: 'Restore/import SQL dump' },
];
};

View File

@@ -86,6 +86,19 @@
});
};
const handleSqlDump = () => {
showModal(ExportDatabaseDumpModal, {
connection: { ...connection, database: name },
});
// exportSqlDump(connection, name);
};
const handleSqlRestore = () => {
showModal(ImportDatabaseDumpModal, {
connection: { ...connection, database: name },
});
};
const handleShowDiagram = async () => {
const db = await getDatabaseInfo({
conid: connection._id,
@@ -158,6 +171,26 @@
openJsonDocument(db, name);
};
const handleGenerateScript = async () => {
const data = await apiCall('database-connections/export-keys', {
conid: connection?._id,
database: name,
options: {
keyPrefix: '',
},
});
if (data.errorMessage) {
showSnackbarError(data.errorMessage);
return;
}
newQuery({
title: 'Export #',
initialData: data,
});
};
async function handleConfirmSql(sql) {
const resp = await apiCall('database-connections/run-script', { conid: connection._id, database: name, sql });
const { errorMessage } = resp || {};
@@ -172,18 +205,25 @@
const commands = _.flatten((apps || []).map(x => x.commands || []));
const isSqlOrDoc =
driver?.databaseEngineTypes?.includes('sql') || driver?.databaseEngineTypes?.includes('document');
return [
{ onClick: handleNewQuery, text: 'New query', isNewQuery: true },
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleNewTable, text: 'New table' },
driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection' },
{ divider: true },
!connection.isReadOnly && { onClick: handleImport, text: 'Import' },
{ onClick: handleExport, text: 'Export' },
{ onClick: handleShowDiagram, text: 'Show diagram' },
{ onClick: handleSqlGenerator, text: 'SQL Generator' },
{ onClick: handleOpenJsonModel, text: 'Open model as JSON' },
{ onClick: handleExportModel, text: 'Export DB model - experimental' },
_.get($currentDatabase, 'connection._id') &&
isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' },
isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' },
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlRestore, text: 'Restore/import SQL dump' },
driver?.supportsDatabaseDump && { onClick: handleSqlDump, text: 'Backup/export SQL dump' },
{ divider: true },
isSqlOrDoc && { onClick: handleShowDiagram, text: 'Show diagram' },
isSqlOrDoc && { onClick: handleSqlGenerator, text: 'SQL Generator' },
isSqlOrDoc && { onClick: handleOpenJsonModel, text: 'Open model as JSON' },
isSqlOrDoc && { onClick: handleExportModel, text: 'Export DB model - experimental' },
isSqlOrDoc &&
_.get($currentDatabase, 'connection._id') &&
(_.get($currentDatabase, 'connection._id') != _.get(connection, '_id') ||
(_.get($currentDatabase, 'connection._id') == _.get(connection, '_id') &&
_.get($currentDatabase, 'name') != _.get(connection, 'name'))) && {
@@ -191,6 +231,8 @@
text: `Compare with ${_.get($currentDatabase, 'name')}`,
},
driver?.databaseEngineTypes?.includes('keyvalue') && { onClick: handleGenerateScript, text: 'Generate script' },
_.get($currentDatabase, 'connection._id') == _.get(connection, '_id') &&
_.get($currentDatabase, 'name') == name && { onClick: handleDisconnect, text: 'Disconnect' },
@@ -231,7 +273,7 @@
import getElectron from '../utility/getElectron';
import openNewTab from '../utility/openNewTab';
import AppObjectCore from './AppObjectCore.svelte';
import { showSnackbarSuccess } from '../utility/snackbar';
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
import { findEngineDriver } from 'dbgate-tools';
import InputTextModal from '../modals/InputTextModal.svelte';
import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders';
@@ -240,6 +282,10 @@
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
import { filterAppsForDatabase } from '../utility/appTools';
import newQuery from '../query/newQuery';
import { exportSqlDump } from '../utility/exportFileTools';
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
import ExportDatabaseDumpModal from '../modals/ExportDatabaseDumpModal.svelte';
export let data;
export let passProps;

View File

@@ -335,9 +335,9 @@
tabComponent,
scriptTemplate,
{ schemaName, pureName, conid, database, objectTypeField },
forceNewTab,
initialData,
icon
forceNewTab?,
initialData?,
icon?
) {
const connection = await getConnectionInfo({ conid });
const tooltip = `${getConnectionLabel(connection)}\n${database}\n${fullDisplayName({

View File

@@ -1,4 +1,6 @@
<script lang="ts" context="module">
import { filterName } from 'dbgate-tools';
interface FileTypeHandler {
icon: string;
format: string;
@@ -74,6 +76,7 @@
};
export const extractKey = data => data.file;
export const createMatcher = ({ file }) => filter => filterName(filter, file);
</script>
<script lang="ts">

View File

@@ -10,9 +10,15 @@
function handleClick() {
if (!disabled) dispatch('click');
}
let domButton;
export function getBoundingClientRect() {
return domButton.getBoundingClientRect();
}
</script>
<input {type} {value} class:disabled {...$$restProps} on:click={handleClick} />
<input {type} {value} class:disabled {...$$restProps} on:click={handleClick} bind:this={domButton} />
<style>
input {

View File

@@ -9,12 +9,14 @@
<style>
label {
border: 1px solid var(--theme-bg-button-inv-2);
padding: 5px;
padding: 4px;
margin: 2px;
width: 100px;
background-color: var(--theme-bg-button-inv);
color: var(--theme-font-inv-1);
border-radius: 2px;
position: relative;
top: 3px;
}
label:hover:not(.disabled) {

View File

@@ -1,13 +1,16 @@
<script context="module">
function getCommandTitle(command) {
let res = command.text;
if (command.keyText || command.keyTextFromGroup) res += ` (${command.keyText || command.keyTextFromGroup})`;
if (command.keyText || command.keyTextFromGroup) {
res += ` (${formatKeyText(command.keyText || command.keyTextFromGroup)})`;
}
return res;
}
</script>
<script lang="ts">
import { commandsCustomized } from '../stores';
import { formatKeyText } from '../utility/common';
import ToolStripButton from './ToolStripButton.svelte';
export let command;

View File

@@ -2,38 +2,58 @@
import { commandsCustomized, visibleCommandPalette } from '../stores';
import { get } from 'svelte/store';
import { runGroupCommand } from './runCommand';
import { isMac, resolveKeyText } from '../utility/common';
export function handleCommandKeyDown(e) {
let keyText = '';
if (e.ctrlKey) keyText += 'Ctrl+';
if (e.shiftKey) keyText += 'Shift+';
if (e.metaKey) keyText += 'Command+';
if (e.shiftKey) keyText += 'Shift+';
if (e.altKey) keyText += 'Alt+';
keyText += e.key;
// console.log('keyText', keyText);
const commandsValue = get(commandsCustomized);
const commandsFiltered: any = Object.values(commandsValue).filter(
let commandsFiltered: any = Object.values(commandsValue).filter(
(x: any) =>
x.keyText &&
x.keyText
resolveKeyText(x.keyText)
.toLowerCase()
.split('|')
.map(x => x.trim())
.includes(keyText.toLowerCase()) &&
(x.disableHandleKeyText == null ||
!x.disableHandleKeyText
!resolveKeyText(x.disableHandleKeyText)
.toLowerCase()
.split('|')
.map(x => x.trim())
.includes(keyText.toLowerCase()))
);
if (commandsFiltered.length > 0) {
if (commandsFiltered.length > 0 && commandsFiltered.find(x => !x.systemCommand)) {
e.preventDefault();
e.stopPropagation();
}
if (
commandsFiltered.length > 1 &&
commandsFiltered.find(x => x.systemCommand) &&
commandsFiltered.find(x => !x.systemCommand)
) {
commandsFiltered = commandsFiltered.filter(x => !x.systemCommand);
}
if (commandsFiltered.every(x => x.systemCommand)) {
return;
}
const notGroup = commandsFiltered.filter(x => x.enabled && !x.isGroupCommand);
if (notGroup.length > 1) {
console.log('Warning, multiple commands mapped to', keyText, notGroup);
}
if (notGroup.length == 1) {
const command = notGroup[0];
if (command.onClick) command.onClick();
@@ -43,6 +63,10 @@
const group = commandsFiltered.filter(x => x.enabled && x.isGroupCommand);
if (group.length > 1) {
console.log('Warning, multiple commands mapped to', keyText, group);
}
if (group.length == 1) {
const command = group[0];
runGroupCommand(command.group);

View File

@@ -18,7 +18,7 @@
category: 'Database',
toolbarName: 'Database search',
name: 'Search',
keyText: isElectronAvailable() ? 'Ctrl+P' : 'F3',
keyText: isElectronAvailable() ? 'CtrlOrCommand+P' : 'F3',
onClick: () => visibleCommandPalette.set('database'),
testEnabled: () => getVisibleCommandPalette() != 'database',
});
@@ -39,6 +39,8 @@
const databaseList = [];
for (const connection of connectionList || []) {
const conid = connection._id;
if (connection.singleDatabase) continue;
if (getCurrentConfig()?.singleDatabase) continue;
const databases = getLocalStorage(`database_list_${conid}`) || [];
for (const db of databases) {
databaseList.push({
@@ -65,9 +67,9 @@
import { databaseObjectIcons, handleDatabaseObjectClick } from '../appobj/DatabaseObjectAppObject.svelte';
import FontIcon from '../icons/FontIcon.svelte';
import {
commands,
commandsCustomized,
currentDatabase,
getCurrentConfig,
getVisibleCommandPalette,
visibleCommandPalette,
} from '../stores';

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