Compare commits
159 Commits
v4.7.4-bet
...
v4.8.8-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31edd48257 | ||
|
|
8e9a14665b | ||
|
|
793bc6b494 | ||
|
|
864b9c9333 | ||
|
|
fb4dd06578 | ||
|
|
3c8a0ebd22 | ||
|
|
3a20f24f7c | ||
|
|
aa8854da93 | ||
|
|
82ae8e23e0 | ||
|
|
4d7887a379 | ||
|
|
30b054dbec | ||
|
|
df743e8ade | ||
|
|
7cc70dc5f8 | ||
|
|
10491868a0 | ||
|
|
8f945dc13f | ||
|
|
d041f88a47 | ||
|
|
e5bbf5eca3 | ||
|
|
66da21804b | ||
|
|
5315a549b0 | ||
|
|
82304f13e7 | ||
|
|
5bcd5d57ba | ||
|
|
b6464dc119 | ||
|
|
4182132dde | ||
|
|
32228d542a | ||
|
|
72f5710dfd | ||
|
|
539c383b21 | ||
|
|
c3289d09c0 | ||
|
|
233fed30cb | ||
|
|
63521002cc | ||
|
|
21642e0a3e | ||
|
|
4945b71c58 | ||
|
|
ed0d63d135 | ||
|
|
2c25669bc7 | ||
|
|
0c94145b18 | ||
|
|
95fb5d51c5 | ||
|
|
a75e931fd5 | ||
|
|
c7c667bbe0 | ||
|
|
97eff2b113 | ||
|
|
0122b0accc | ||
|
|
6c718981d6 | ||
|
|
f78d37159e | ||
|
|
bf1881595c | ||
|
|
4e8c92eb36 | ||
|
|
34a0cb095b | ||
|
|
f606d0c41c | ||
|
|
8002d734bc | ||
|
|
be6d572ce2 | ||
|
|
c3baa88d9e | ||
|
|
5ef06abd1a | ||
|
|
e86a34ab53 | ||
|
|
c2b715e3f7 | ||
|
|
666f1a3159 | ||
|
|
1ee66047d4 | ||
|
|
8032bf272b | ||
|
|
d52cfadfc4 | ||
|
|
374c820567 | ||
|
|
cc639df566 | ||
|
|
5a42e8e963 | ||
|
|
0187bb74ee | ||
|
|
bf4b7beadb | ||
|
|
b254c90f33 | ||
|
|
89fdfbe8c1 | ||
|
|
a37c81be88 | ||
|
|
abe9694d05 | ||
|
|
c55c1f3aaf | ||
|
|
ce6b750a1c | ||
|
|
f83d45356f | ||
|
|
589ff1ad38 | ||
|
|
ba97f50273 | ||
|
|
e52fbd5034 | ||
|
|
e29d3a6143 | ||
|
|
7313fa16f6 | ||
|
|
18437d1be7 | ||
|
|
2b39c85918 | ||
|
|
5135b985c9 | ||
|
|
05619faa7a | ||
|
|
12a638af3b | ||
|
|
38e05bf8e0 | ||
|
|
6d92de6930 | ||
|
|
90f8d349fc | ||
|
|
5379e86d6e | ||
|
|
85e449953f | ||
|
|
30e52723dd | ||
|
|
467918bcbb | ||
|
|
73d17504c1 | ||
|
|
7a3007deb2 | ||
|
|
2a46ff78bb | ||
|
|
fa193f0e57 | ||
|
|
f702513bb9 | ||
|
|
72462376b1 | ||
|
|
a46ef7f0d0 | ||
|
|
9f5013c6da | ||
|
|
5ea6c56752 | ||
|
|
cbb38b8edc | ||
|
|
045f6c6a47 | ||
|
|
334ab504cf | ||
|
|
66db28010c | ||
|
|
807392fc57 | ||
|
|
1a32d88312 | ||
|
|
1a76cc0979 | ||
|
|
bcdaf84739 | ||
|
|
39296a852e | ||
|
|
1a035ca168 | ||
|
|
c0b365602b | ||
|
|
5aac142e4c | ||
|
|
25380ee0a8 | ||
|
|
0b3b18ceda | ||
|
|
9cecebe8bc | ||
|
|
c0227ecce1 | ||
|
|
1d37950b37 | ||
|
|
fe277f5ffa | ||
|
|
1b52a2a0fc | ||
|
|
a062073a5f | ||
|
|
44e52dfa9c | ||
|
|
0323264bbd | ||
|
|
aacedba450 | ||
|
|
aac768b158 | ||
|
|
d4a7ae13e1 | ||
|
|
b8206a7a02 | ||
|
|
4c2f6c9b65 | ||
|
|
d58d46feac | ||
|
|
760edc7eca | ||
|
|
7c0f33383f | ||
|
|
a20a34680d | ||
|
|
0e8166577f | ||
|
|
11c82b1aac | ||
|
|
61f24c3408 | ||
|
|
e25657bd43 | ||
|
|
4bd7cd26d0 | ||
|
|
e06894372f | ||
|
|
1f0ae98c88 | ||
|
|
c0fdcf2fd1 | ||
|
|
8d31130737 | ||
|
|
9e3991556a | ||
|
|
fc08353225 | ||
|
|
25556f0d3e | ||
|
|
0fb3817af6 | ||
|
|
d8e840f127 | ||
|
|
4270722557 | ||
|
|
b3cfff0ae8 | ||
|
|
2f5f0ab54c | ||
|
|
4c856c5e36 | ||
|
|
5c8ae85c54 | ||
|
|
5b39576e61 | ||
|
|
735c48902f | ||
|
|
613ac3f0e5 | ||
|
|
1aecda6d9f | ||
|
|
38dfad4dfc | ||
|
|
25ae5bf048 | ||
|
|
f3bfe58c58 | ||
|
|
ff714b1f8a | ||
|
|
93552585f7 | ||
|
|
ec7641dbd6 | ||
|
|
e2308f501b | ||
|
|
45277e34c9 | ||
|
|
ba77b32e9a | ||
|
|
c05ef42bf9 | ||
|
|
b0e0197346 | ||
|
|
4c411b048d |
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -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
|
||||
|
||||
10
.github/workflows/build-app-beta.yaml
vendored
@@ -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'
|
||||
|
||||
12
.github/workflows/build-app.yaml
vendored
@@ -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
|
||||
|
||||
14
.github/workflows/build-npm.yaml
vendored
@@ -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: |
|
||||
|
||||
11
.github/workflows/run-tests.yaml
vendored
@@ -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:
|
||||
|
||||
63
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://www.npmjs.com/package/dbgate)
|
||||
[](https://www.npmjs.com/package/dbgate-serve)
|
||||

|
||||
[](https://snapcraft.io/dbgate)
|
||||
[](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.)
|
||||
|
||||
12
app/entitlements.mac.plist
Normal 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>
|
||||
BIN
app/icon.ico
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 158 KiB |
BIN
app/icon.png
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 36 KiB |
BIN
app/icon32.png
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 15 KiB |
BIN
app/icon512.png
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 18 KiB |
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
|
||||
264
app/yarn.lock
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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');
|
||||
|
||||
11
package.json
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -37,6 +37,7 @@ module.exports = {
|
||||
// hideAppEditor: !!process.env.HIDE_APP_EDITOR,
|
||||
allowShellConnection: platformInfo.allowShellConnection,
|
||||
allowShellScripting: platformInfo.allowShellConnection,
|
||||
isDocker: platformInfo.isDocker,
|
||||
permissions,
|
||||
login,
|
||||
...currentVersion,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)])),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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' };
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
68
packages/api/src/proc/sshForwardProcess.js
Normal 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 };
|
||||
38
packages/api/src/shell/dumpDatabase.js
Normal 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;
|
||||
57
packages/api/src/shell/importDatabase.js
Normal 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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,27 +1,2 @@
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](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
|
||||
|
||||

|
||||
This package is obsolete, please use [dbgate-serve](https://www.npmjs.com/package/dbgate-serve) package instead
|
||||
|
||||
@@ -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();
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
1
packages/query-splitter/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
lib
|
||||
@@ -1,53 +0,0 @@
|
||||
[](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)
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
};
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { splitQuery } from './splitQuery';
|
||||
export * from './options';
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
@@ -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`']);
|
||||
});
|
||||
@@ -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
@@ -0,0 +1,27 @@
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](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
|
||||
|
||||

|
||||
12
packages/serve/bin/dbgate-serve.js
Executable 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();
|
||||
32
packages/serve/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
22
packages/types/engines.d.ts
vendored
@@ -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>;
|
||||
|
||||
1
packages/types/extensions.d.ts
vendored
@@ -3,6 +3,7 @@ import { EngineDriver } from './engines';
|
||||
export interface FileFormatDefinition {
|
||||
storageType: string;
|
||||
extension: string;
|
||||
extensions?: string[];
|
||||
name: string;
|
||||
readerFunc?: string;
|
||||
writerFunc?: string;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 62 KiB |
@@ -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}
|
||||
|
||||
@@ -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' },
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||