Compare commits
524 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0f1a8f8b1 | |||
| b43fe93300 | |||
| 3856b4e725 | |||
| 153a4bca42 | |||
| 20fccf51d9 | |||
| a5d37eb528 | |||
| 6af21b8bae | |||
| 3b047dbe6d | |||
| 007b40bf9b | |||
| 0db9ae7cb1 | |||
| c7e1e294ef | |||
| 1bf9110f4b | |||
| bb3dad6e1c | |||
| d3a019e8a3 | |||
| 48f8908040 | |||
| 829ec8d25b | |||
| 00aaaad855 | |||
| c48b058b9d | |||
| b553dbb6b9 | |||
| 2db17f9eca | |||
| bcc1f91352 | |||
| 1c0c2bbc71 | |||
| d236782795 | |||
| 54cf6ad411 | |||
| 0dac1ada5f | |||
| 82b63c70ed | |||
| e84d231a10 | |||
| 362ad344d2 | |||
| dcd8e8e43b | |||
| d43304792a | |||
| e4e01c6e1e | |||
| ccb1c26905 | |||
| 6c2ee5ffdb | |||
| 6d6c360521 | |||
| 0459ca886b | |||
| 29e6dad713 | |||
| c160fdb628 | |||
| 554be51546 | |||
| ff52430e1e | |||
| 853eee6701 | |||
| 33062da66d | |||
| e3fe5a2beb | |||
| 573e404612 | |||
| 91c88bd92d | |||
| 475f82a35e | |||
| 0548bae7af | |||
| 0413f4b4d9 | |||
| 9e9991c675 | |||
| 21502bda65 | |||
| 69e1c6c625 | |||
| fcedeb2316 | |||
| a23ff752a3 | |||
| 138b0414f2 | |||
| 87988d5c3a | |||
| 41cf7009b3 | |||
| 18860c823d | |||
| 55cc51d24a | |||
| 2a49eaab12 | |||
| 394c6028c9 | |||
| d4bd6e03c9 | |||
| 1a94222262 | |||
| 94b41fecbc | |||
| 9ed6932c1e | |||
| e748591c10 | |||
| fca6b87cb9 | |||
| c23ecfff47 | |||
| 8738665dcf | |||
| a05fc90579 | |||
| 71f7a705c4 | |||
| 9cb2d397ad | |||
| 484e7c27a2 | |||
| acdba0c52c | |||
| 943544958a | |||
| e78420b1b0 | |||
| 69d639b9ea | |||
| a540220c05 | |||
| b1ddcbfbfc | |||
| 87aaa281e4 | |||
| 704733d80d | |||
| a50458494e | |||
| 2a980a7892 | |||
| a3762c6caa | |||
| d6ba822338 | |||
| f146d70e2b | |||
| d62177d996 | |||
| a1993214e2 | |||
| 70411b764b | |||
| cd0fb0fdf2 | |||
| 9713c9b88e | |||
| d5118909d1 | |||
| bb41236a5f | |||
| 9d84c0f213 | |||
| d45fbcb8c8 | |||
| 4762597741 | |||
| 9c27c224ec | |||
| 2268d6126b | |||
| bbc50ea3fb | |||
| 11985004b5 | |||
| 4f58d2ff80 | |||
| 987fe6095a | |||
| d8fcbc8c17 | |||
| 833610c88b | |||
| 218478c128 | |||
| 3f8ff91e2c | |||
| 11436c065c | |||
| f1c70f6f82 | |||
| c66ff5820c | |||
| 4c87ad50b1 | |||
| 6c5f5a7cfb | |||
| 9876a76836 | |||
| 952bdc4baa | |||
| 2afa7a5f58 | |||
| bc9e8a2ea6 | |||
| e2dcfe9940 | |||
| 638b04877d | |||
| 2a9c67d2f6 | |||
| 2c9d424fc8 | |||
| 4e76f10175 | |||
| 5f2afc037e | |||
| 50e61cdce1 | |||
| 586f2fed21 | |||
| 020b4163d7 | |||
| 6b0e1e322a | |||
| 0d0bd29812 | |||
| 4e4447de8a | |||
| 15c9e93e8a | |||
| 5531705433 | |||
| dfadf0653d | |||
| 742b68453a | |||
| 155406827e | |||
| a97e6dbcab | |||
| d4989c75ca | |||
| b7b9dde5ae | |||
| 34f2fb2a0a | |||
| 965a967450 | |||
| 40872699c6 | |||
| ec90a8b952 | |||
| 90c4b44fdb | |||
| df5062c9a5 | |||
| 437155e4c5 | |||
| 5ebee161ae | |||
| 10c77ad153 | |||
| 5e59926556 | |||
| 4b1b61328a | |||
| 4c6d9f0660 | |||
| a5a7447cec | |||
| dd373f9db9 | |||
| bb0f5e4404 | |||
| c77bc820d4 | |||
| a1ab47a6f9 | |||
| efc07280a6 | |||
| dcb4c5071a | |||
| 7b625c6073 | |||
| 489c9a905c | |||
| 75c578de47 | |||
| f7c4bbc708 | |||
| 9c1227273c | |||
| 47a045fc24 | |||
| 9e9df60d37 | |||
| 93b7a9a674 | |||
| b96576ba6f | |||
| 0524b4c5b6 | |||
| b7663e2e06 | |||
| 24f4e1d898 | |||
| 9089f78593 | |||
| 8f90dad303 | |||
| c823e18699 | |||
| 73bfac2bfb | |||
| 08b5bce03c | |||
| ddf8a5806c | |||
| eceab2dde9 | |||
| 9c7df42948 | |||
| 321d5d71de | |||
| d4a35fb414 | |||
| 21feb3a042 | |||
| 9adb4b41c6 | |||
| 3b3e81e3f7 | |||
| dfa8ca6797 | |||
| 0af207d330 | |||
| 49337a4112 | |||
| 7cd26c4fe4 | |||
| 159ba72129 | |||
| cfb772c717 | |||
| 500c1c76ba | |||
| 834eeabd3f | |||
| 8770034bf5 | |||
| 423f876d68 | |||
| af54c958ba | |||
| 4e16119835 | |||
| d6f9db48aa | |||
| 2063005d5c | |||
| c2c54856ff | |||
| cedb740fb0 | |||
| 913f89e970 | |||
| 7d6bf90a0a | |||
| 8a4275fb09 | |||
| d5ebea3764 | |||
| a93aff1bb7 | |||
| c193955fbe | |||
| 5f97f7d922 | |||
| 54d17a67d4 | |||
| 0c94d7fcac | |||
| fca23f65de | |||
| a35d5525a3 | |||
| 904d35d26a | |||
| 929c08e094 | |||
| 97a27381f2 | |||
| f4fe5b9b53 | |||
| 00d5b25baa | |||
| c6e95dbb6a | |||
| da6bd9b475 | |||
| 2eebe44f87 | |||
| 3dd99a44cb | |||
| ec2acebdc9 | |||
| e49c0169da | |||
| 49f22e1a3b | |||
| 423644e9d9 | |||
| b64b6be68a | |||
| d3c4c18b62 | |||
| fe5826bc8e | |||
| 7709e24ccd | |||
| b91cf18aee | |||
| ae9c4b4f98 | |||
| 3d25a51cf9 | |||
| 18e7171038 | |||
| 8cf014efa4 | |||
| 78d71602bf | |||
| dcfd6ee1dc | |||
| eb4ecb4cf8 | |||
| bc54564d64 | |||
| 1c7052810a | |||
| 4bfba2ec02 | |||
| c2dc4d76ba | |||
| ce44e271ae | |||
| ef5bfb5a89 | |||
| 7acea0f4ac | |||
| 593e61abb9 | |||
| a0aa508e8d | |||
| ca517f9c73 | |||
| ad0e02de5d | |||
| ea05ae0079 | |||
| 689eb7baa2 | |||
| a4387155e7 | |||
| 565a60e638 | |||
| 5dba5a6dfd | |||
| c497c1ceca | |||
| 5929a01010 | |||
| 182b0e0a75 | |||
| 2cc74b594e | |||
| e9430988f4 | |||
| f30bd0a894 | |||
| 1c0a8cad56 | |||
| 8a4fd302d2 | |||
| db7f8d6a74 | |||
| 4f1eb4003a | |||
| 3efaac7d1f | |||
| d9387bef1f | |||
| a101f21483 | |||
| 8a0d10e50d | |||
| fe1fc7923f | |||
| f0802dc471 | |||
| 30ade5867c | |||
| ea54673497 | |||
| 2ffd729465 | |||
| ef910f43a6 | |||
| fc333167ac | |||
| 390447c948 | |||
| 1bb5f4974d | |||
| 48e3cf1be5 | |||
| 1e540b3fe9 | |||
| 60c1090d6c | |||
| 71bea87a7a | |||
| a03261bfd4 | |||
| 704a04e9bb | |||
| 28c1421294 | |||
| 7a5bcc62c8 | |||
| 321eedefea | |||
| daf9e9d18b | |||
| dd7db5904c | |||
| 6bddf3aa83 | |||
| 9743569ca7 | |||
| 40ed020c0a | |||
| 63ac08cc27 | |||
| e16b0ef61f | |||
| 14f9a40851 | |||
| ba6abd1e64 | |||
| 4ffc5842bb | |||
| 4cb304a41c | |||
| abdbe4122f | |||
| b2e37e88ea | |||
| 6a28ceaa51 | |||
| c15f859eae | |||
| f717dfa3b5 | |||
| ab7510c8e8 | |||
| 3601ac21b4 | |||
| 65bd5a60ef | |||
| 6a9bd8248c | |||
| 98ff6db701 | |||
| 7b67576131 | |||
| d0d6d86bb9 | |||
| 1f057fb0a9 | |||
| 75a429b74f | |||
| d34524c3d0 | |||
| bdfa66d37d | |||
| 1d5d87e26a | |||
| 9f3aadc17d | |||
| 58f213d042 | |||
| 89dbf38962 | |||
| 670e3d127e | |||
| 72181e70a1 | |||
| c3ac836fa9 | |||
| b0deba4bae | |||
| d08fc85459 | |||
| 417ec9fcd2 | |||
| f9d4a9a3a0 | |||
| eab870c237 | |||
| 521199ee1a | |||
| 0d1a6e96f3 | |||
| 1076fb8391 | |||
| 114dc0b543 | |||
| 728ca72cc1 | |||
| e243ecd96a | |||
| 2defdc3f28 | |||
| 7aeef55a58 | |||
| 5e8967da52 | |||
| 679145a394 | |||
| 9b012c187a | |||
| 9ce1fdd59e | |||
| c0d0a00615 | |||
| c67c08bd69 | |||
| 6e846797b9 | |||
| a3ad98d2a9 | |||
| c7dbf333c7 | |||
| 19392e9406 | |||
| 818de9b111 | |||
| 0292a37b16 | |||
| 8b9031b0c2 | |||
| d88591032e | |||
| 2c6a59638b | |||
| 8312415430 | |||
| fdb14d687b | |||
| a20b351938 | |||
| f8016d26ec | |||
| ad186f5efb | |||
| 8e26918975 | |||
| e0303aa77e | |||
| 6a02c4ebaa | |||
| 2c73ab6bc1 | |||
| b60714f30c | |||
| f3163617e0 | |||
| 834be32676 | |||
| 0f6637188b | |||
| ef0921ecf5 | |||
| 1ffa613e09 | |||
| c0c8cd88e3 | |||
| 2666717c3a | |||
| 777abbc097 | |||
| e4db985ef9 | |||
| 6afaa6f856 | |||
| 1325851bcf | |||
| fb7da60127 | |||
| ecde9cb6bd | |||
| 6b06ed5baf | |||
| 2aa965cf3b | |||
| fc11fe1e8d | |||
| e33e14bd5f | |||
| 0c7fc0b7b6 | |||
| 5904d45c44 | |||
| 5d997fc1c9 | |||
| 4693564ffa | |||
| 3377929b30 | |||
| a35f6f2629 | |||
| cb264ac6cc | |||
| 9236e1a6c2 | |||
| df359aea58 | |||
| fdf60b5267 | |||
| bd3c18d883 | |||
| 18bf6e5979 | |||
| edaf9676e4 | |||
| bd524d345a | |||
| 0a39a6829c | |||
| 5c7a011efb | |||
| 4e350e99c4 | |||
| a714f7ae54 | |||
| a17b76c570 | |||
| 54d476a972 | |||
| 255c3e5ef4 | |||
| 059eabf2fa | |||
| 79fdde73ae | |||
| 84e475192e | |||
| 3907b1ae8b | |||
| dcfefc78a2 | |||
| d3039a9248 | |||
| 31dd80b79a | |||
| 8d6d1d979e | |||
| fe1c5f5801 | |||
| df976a84d2 | |||
| 420e94600e | |||
| 9940bd5177 | |||
| 45d99a4126 | |||
| c2b7c775c0 | |||
| 51ba9d3b5a | |||
| 8396e726ec | |||
| cb67b57faf | |||
| 99381536d7 | |||
| a9cb9f1874 | |||
| 420a58380a | |||
| 75ca3cbb11 | |||
| a5c1966a94 | |||
| ca4ff95316 | |||
| a3294950a4 | |||
| 29355a6d3e | |||
| add0ba09c3 | |||
| 005ae87309 | |||
| 5f372a1d0f | |||
| ecce75960a | |||
| 72cc510c64 | |||
| 7e39b8c2a0 | |||
| ed4ef4d999 | |||
| 0e6b8b4f73 | |||
| 69fd9bbc67 | |||
| e561bf38f6 | |||
| b3d436ddf9 | |||
| ac59665be4 | |||
| df74dd114b | |||
| 213ee01fa6 | |||
| e253cfb1b0 | |||
| 532c64840b | |||
| c5bcac2755 | |||
| a23c882473 | |||
| e4ad9acb68 | |||
| 427f23e1e0 | |||
| c9fefd14fd | |||
| e2ea2809b6 | |||
| effb61d43d | |||
| 1495d822d5 | |||
| acebc0c692 | |||
| 1f1a3072db | |||
| 7f115cf571 | |||
| bd09e1bb5d | |||
| b24d95fb1f | |||
| 1ce37c6ea8 | |||
| 6b642c6634 | |||
| 11f5bb8b93 | |||
| 5935c9a9e7 | |||
| 6e60cf8691 | |||
| 19a39d4a83 | |||
| fa29bc65dc | |||
| 8a19d7c4bc | |||
| a1f9b1c53e | |||
| 54320aa001 | |||
| b9cb8c3a45 | |||
| 451af5d09f | |||
| 37cc86f8d2 | |||
| 7f5143aac2 | |||
| ee696b9efe | |||
| dd97d13620 | |||
| 0b313ed1cf | |||
| 1d8df1f4fc | |||
| 7c7c334d67 | |||
| a44f4f6208 | |||
| 3da222f3d1 | |||
| 5a216c0c3e | |||
| 00a5745b62 | |||
| 91ec4c2864 | |||
| 7fb365ddb4 | |||
| afebddd2a3 | |||
| add6f351a5 | |||
| 293f476c2a | |||
| 777c9a99a2 | |||
| 6a8a47cf03 | |||
| 8b0d77569c | |||
| 40d2696b43 | |||
| 0f276b0422 | |||
| 59d728a80b | |||
| 715c9e634b | |||
| e1ad90da49 | |||
| ffbcbc6d06 | |||
| 946ec7eb32 | |||
| 011ca0a5c4 | |||
| 72619a9902 | |||
| 97a4cd1653 | |||
| 22d37bda5d | |||
| cc049368e3 | |||
| 14e139a163 | |||
| 8f98b352da | |||
| a9c38d5782 | |||
| dd6cdeb946 | |||
| c9227d1e37 | |||
| c06965c79f | |||
| ccd9a0fa70 | |||
| d1aef572bd | |||
| c5e75dfe3e | |||
| 943f9c1c24 | |||
| cd23687428 | |||
| f7d559ad20 | |||
| 8b340e9b7e | |||
| 4328107a1d | |||
| ad4fa77e46 | |||
| a21b61cc7e | |||
| 26b1c69f12 | |||
| cac85d776a | |||
| 93af08626a | |||
| d7fac5bc6a | |||
| d1033f0e82 | |||
| 9600c213ef | |||
| f2dbe1f103 | |||
| f60ee04883 | |||
| 2cd2546f3f | |||
| 9fe0c2ac86 | |||
| 2f2310a883 | |||
| c2ccf27adf | |||
| 9a275d62c0 | |||
| 35398dd401 | |||
| 99746f48df | |||
| 83eec6a37a | |||
| cca6b77ba5 | |||
| 69b9ce8afc | |||
| 63af42b69d | |||
| 0821d63c9f | |||
| 904a9b54c1 | |||
| 2000a4cf2b | |||
| fcc1459dee | |||
| b44f49a492 |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve DbGate
|
||||
title: 'BUG: Say something here'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version Information (please complete the following information):**
|
||||
- OS: [e.g. Windows/Mac/Ubuntu]
|
||||
- App Version [see Help -> About]
|
||||
- Install source [e.g. installer/SNAP/Docker/NPM]
|
||||
- Type - Web/Application
|
||||
- Database engine: [e.g. MySQL/PostgreSQL/SQL Server]
|
||||
|
||||
**Additional context**
|
||||
Anything else you think might be helpful
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for DbGate
|
||||
title: 'FEAT: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
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 context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about how to do something
|
||||
title: 'QUESTION: Summary of your question'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Details:**
|
||||
Details about your question
|
||||
|
||||
**Version Information [might be relevant to your issue]**
|
||||
Operating System:
|
||||
App Version [help -> about]:
|
||||
|
||||
**Screenshot [if appropriate]**:
|
||||
A screenshot of the app if that helps
|
||||
@@ -0,0 +1,85 @@
|
||||
name: Electron app
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, windows-2016]
|
||||
# os: [macOS-10.14, windows-2016, ubuntu-18.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
- name: Install Snapcraft
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
uses: samuelmeuli/action-snapcraft@v1
|
||||
- name: Publish
|
||||
run: |
|
||||
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 }}
|
||||
|
||||
- name: Save snap login
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: 'echo "$SNAPCRAFT_LOGIN" > snapcraft.login'
|
||||
shell: bash
|
||||
env:
|
||||
SNAPCRAFT_LOGIN: ${{secrets.SNAPCRAFT_LOGIN}}
|
||||
|
||||
- name: publishSnap
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
snapcraft login --with snapcraft.login
|
||||
snapcraft upload --release=beta app/dist/*.snap
|
||||
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir artifacts
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
mv app/dist/*.AppImage artifacts/ || true
|
||||
mv app/dist/*.deb artifacts/ || true
|
||||
mv app/dist/*.snap artifacts/ || true
|
||||
# mv app/dist/*.dmg artifacts/ || true
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{ matrix.os }}
|
||||
path: artifacts
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: 'artifacts/**'
|
||||
prerelease: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -3,7 +3,8 @@ name: Electron app
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
# branches:
|
||||
# - production
|
||||
@@ -35,16 +36,37 @@ jobs:
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
- name: Install Snapcraft
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
uses: samuelmeuli/action-snapcraft@v1
|
||||
- name: Publish
|
||||
run: |
|
||||
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 }}
|
||||
|
||||
- name: generatePadFile
|
||||
run: |
|
||||
yarn generatePadFile
|
||||
|
||||
- name: Save snap login
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: 'echo "$SNAPCRAFT_LOGIN" > snapcraft.login'
|
||||
shell: bash
|
||||
env:
|
||||
SNAPCRAFT_LOGIN: ${{secrets.SNAPCRAFT_LOGIN}}
|
||||
|
||||
- name: publishSnap
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
snapcraft login --with snapcraft.login
|
||||
snapcraft upload --release=stable app/dist/*.snap
|
||||
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir artifacts
|
||||
@@ -58,6 +80,7 @@ jobs:
|
||||
mv app/dist/*.AppImage artifacts/ || true
|
||||
mv app/dist/*.deb artifacts/ || true
|
||||
mv app/dist/*.dmg artifacts/ || true
|
||||
mv app/dist/*.snap artifacts/dbgate-latest.snap || true
|
||||
|
||||
# - name: Copy artifacts Linux, MacOs
|
||||
# if: matrix.os != 'windows-2016'
|
||||
|
||||
@@ -5,7 +5,8 @@ name: Docker image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
# on:
|
||||
# push:
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
name: NPM packages
|
||||
|
||||
# on: [push]
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# - 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
|
||||
|
||||
# on:
|
||||
# push:
|
||||
# branches:
|
||||
# - production
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
# os: [macOS-10.14, windows-2016, ubuntu-18.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.x
|
||||
|
||||
- name: Configure NPM token
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
|
||||
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install
|
||||
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
|
||||
- name: Publish types
|
||||
working-directory: packages/types
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish tools
|
||||
working-directory: packages/tools
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish sqltree
|
||||
working-directory: packages/sqltree
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish api
|
||||
working-directory: packages/api
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish datalib
|
||||
working-directory: packages/datalib
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish filterparser
|
||||
working-directory: packages/filterparser
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish web
|
||||
working-directory: packages/web
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate
|
||||
working-directory: packages/dbgate
|
||||
run: |
|
||||
npm publish
|
||||
@@ -12,6 +12,10 @@ node_modules
|
||||
build
|
||||
dist
|
||||
|
||||
app/packages/web/public
|
||||
docker/public
|
||||
docker/bundle.js
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
@@ -22,3 +26,6 @@ dist
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
app/src/nativeModulesContent.js
|
||||
packages/api/src/nativeModulesContent.js
|
||||
.VSCodeCounter
|
||||
@@ -0,0 +1,28 @@
|
||||
# ChangeLog
|
||||
|
||||
### 4.0.3
|
||||
- FIX: fixes for FireFox (mainly incorrent handle of bind:clientHeight, replaces with resizeobserver)
|
||||
### 4.0.2
|
||||
- FIX: fixed docker and NPM build
|
||||
### 4.0.0
|
||||
- CHANGED: Excahnged React with Svelte. Changed theme colors. Huge speed and memory optimalization
|
||||
- ADDED: SQL Generator (CREATE, INSERT, DROP)
|
||||
- ADDED: Command palette (F1). Introduced commands, extended some context menus
|
||||
- ADDED: New keyboard shortcuts
|
||||
- ADDED: Switch to recent database feature
|
||||
- ADDED: Macros from free table editor are available also in table data editor
|
||||
- CHANGED: Cell data preview is now in left widgets panel
|
||||
- CHANGED: Toolbar refactor
|
||||
- FIX: Solved reconnecting expired connection
|
||||
|
||||
### 3.9.6
|
||||
- ADDED: Connect using SSH Tunnel
|
||||
- ADDED: Connect using SSL
|
||||
- ADDED: Database connection dialog redesigned
|
||||
- ADDED: #63 Ctrl+Enter runs query
|
||||
- ADDED: Published dbgate NPM package
|
||||
- ADDED: SQL editor context menu
|
||||
- FIX: #62 - import, export executed from SNAP installs didn't work
|
||||
|
||||
### 3.9.5
|
||||
- Start point of changelog
|
||||
@@ -1,34 +1,46 @@
|
||||
[](https://www.npmjs.com/package/dbgate)
|
||||
[](https://snapcraft.io/dbgate)
|
||||
[](https://snapcraft.io/dbgate)
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](https://paypal.me/JanProchazkaCz/30eur)
|
||||
[](https://www.npmjs.com/package/dbgate-api)
|
||||
|
||||
# DbGate - database administration tool
|
||||
|
||||
DbGate is fast and efficient database administration tool. It is focused to work with data (filtering, editing, master/detail views etc.)
|
||||
DbGate is fast and easy to use database manager. Works with MySQL, PostgreSQL and SQL Server.
|
||||
|
||||
**Try it online** - https://demo.dbgate.org - online demo application
|
||||
* 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)
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
* Support for Microsoft SQL Server, Postgre SQL, MySQL
|
||||
* Table data browsing - filtering, sorting, related columns using foreign keys
|
||||
* Master/detail views
|
||||
* Browsing objects - tables, views, procedures, functions
|
||||
* Connect to Microsoft SQL Server, Postgre SQL, MySQL
|
||||
* Table data editing, with SQL change script preview
|
||||
* Master/detail views
|
||||
* Query designer
|
||||
* Form view for comfortable work with tables with many columns
|
||||
* Explore tables, views, procedures, functions
|
||||
* SQL editor, execute SQL script, SQL code formatter, SQL code completion, SQL join wizard
|
||||
* 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
|
||||
* Free table editor - quick table data editing (cleanup data after import/before export, prototype tables etc.)
|
||||
* Archives - backup your data in JSON files on local filesystem (or on DbGate server, when using web application)
|
||||
* Light and dark theme
|
||||
* Charts
|
||||
* For detailed info, how to run DbGate in docker container, visit [docker hub](https://hub.docker.com/r/dbgate/dbgate)
|
||||
* Extensible plugin architecture
|
||||
|
||||

|
||||
## Why is DbGate different
|
||||
There are many database managers now, so why DbGate?
|
||||
* Works everywhere - Windows, Linux, Mac, Web browser (+mobile web is planned), without compromises in features
|
||||
* Based on standalone NPM packages, scripts can be run without DbGate (example - [CSV export](https://www.npmjs.com/package/dbgate-plugin-csv) )
|
||||
* Many data browsing functions based using foreign keys - master/detail, expand columns, expandable form view (on screenshot above)
|
||||
|
||||
## Design goals
|
||||
* Application simplicity - DbGate takes the best and only the best from old [DbGate](http://www.jenasoft.com/dbgate), [DatAdmin](http://www.jenasoft.com/datadmin) and [DbMouse](http://www.jenasoft.com/dbmouse) .
|
||||
* Minimal dependencies
|
||||
* Frontend - React, styled-components, socket.io
|
||||
* Frontend - Svelte, socket.io
|
||||
* Backend - NodeJs, ExpressJs, socket.io, database connection drivers
|
||||
* JavaScript + TypeScript
|
||||
* App - electron
|
||||
@@ -38,36 +50,12 @@ DbGate is fast and efficient database administration tool. It is focused to work
|
||||
## Plugins
|
||||
Plugins are standard NPM packages published on [npmjs.com](https://www.npmjs.com).
|
||||
See all [existing DbGate plugins](https://www.npmjs.com/search?q=keywords:dbgateplugin).
|
||||
Visit [dbgate generator homepage](https://github.com/dbshell/generator-dbgate) to see, how to create your own plugin.
|
||||
Visit [dbgate generator homepage](https://github.com/dbgate/generator-dbgate) to see, how to create your own plugin.
|
||||
|
||||
Currently following extensions can be implemented using plugins:
|
||||
- File format parsers/writers
|
||||
- Database engine connectors
|
||||
|
||||
## How Can I Contribute?
|
||||
You're welcome to contribute to this project! Below are some ideas, how to contribute:
|
||||
|
||||
* Create plugins for new import/export formats
|
||||
* Bug fixing
|
||||
* Test Mac edition
|
||||
* Improve linux package build, add to APT repository
|
||||
* Auto-upgrade of electron application
|
||||
|
||||
Any help is appreciated!
|
||||
|
||||
Feel free to report issues and open merge requests.
|
||||
|
||||
## Roadmap
|
||||
|
||||
| Feature | Complexity | Schedule |
|
||||
|---|---|---|
|
||||
| Query designer | medium | december 2020 |
|
||||
| Table designer (structure editor) | big | january 2021 |
|
||||
| Filter SQL result sets | small | november 2020 |
|
||||
| Filtering, sorting in free table editor | small | november 2020 |
|
||||
| Using tedious driver instead of mssql | small | january 2021 |
|
||||
| Support for SQLite | big | 2021 |
|
||||
|
||||
## How to run development environment
|
||||
|
||||
```sh
|
||||
@@ -82,7 +70,7 @@ yarn lib
|
||||
|
||||
Open http://localhost:5000 in your browser
|
||||
|
||||
You could run electron app, using this server:
|
||||
You could run electron app (requires running localhost:5000):
|
||||
```sh
|
||||
cd app
|
||||
yarn
|
||||
@@ -90,7 +78,7 @@ yarn start
|
||||
```
|
||||
|
||||
## How to run built electron app locally
|
||||
This mode is very similar to production run of electron app. Electron app forks process with API on dynamically allocated port, works with compiled javascript files.
|
||||
This mode is very similar to production run of electron app. Electron app forks process with API on dynamically allocated port, works with compiled javascript files (doesn't use localhost:5000)
|
||||
|
||||
```sh
|
||||
cd app
|
||||
@@ -106,13 +94,12 @@ yarn start:app:local
|
||||
## Packages
|
||||
Some dbgate packages can be used also without DbGate. You can find them on [NPM repository](https://www.npmjs.com/search?q=keywords:dbgate)
|
||||
|
||||
* [api](https://github.com/dbshell/dbgate/tree/master/packages/api) - backend, Javascript, ExpressJS [](https://www.npmjs.com/package/dbgate-api)
|
||||
* [datalib](https://github.com/dbshell/dbgate/tree/master/packages/datalib) - TypeScript library for utility classes
|
||||
* [app](https://github.com/dbshell/dbgate/tree/master/app) - application (JavaScript)
|
||||
structure, creating specific queries (JavaScript) [](https://www.npmjs.com/package/dbgate-engines)
|
||||
* [filterparser](https://github.com/dbshell/dbgate/tree/master/packages/filterparser) - TypeScript library for parsing data filter expressions using parsimmon
|
||||
* [sqltree](https://github.com/dbshell/dbgate/tree/master/packages/sqltree) - JSON representation of SQL query, functions converting to SQL (TypeScript) [](https://www.npmjs.com/package/dbgate-sqltree)
|
||||
* [types](https://github.com/dbshell/dbgate/tree/master/packages/types) - common TypeScript definitions [](https://www.npmjs.com/package/dbgate-types)
|
||||
* [web](https://github.com/dbshell/dbgate/tree/master/packages/web) - frontend in React (JavaScript)
|
||||
* [tools](https://github.com/dbshell/dbgate/tree/master/packages/tools) - various tools [](https://www.npmjs.com/package/dbgate-tools)
|
||||
* [api](https://github.com/dbgate/dbgate/tree/master/packages/api) - backend, Javascript, ExpressJS [](https://www.npmjs.com/package/dbgate-api)
|
||||
* [datalib](https://github.com/dbgate/dbgate/tree/master/packages/datalib) - TypeScript library for utility classes [](https://www.npmjs.com/package/dbgate-datalib)
|
||||
* [app](https://github.com/dbgate/dbgate/tree/master/app) - application (JavaScript) structure, creating specific queries (JavaScript)
|
||||
* [filterparser](https://github.com/dbgate/dbgate/tree/master/packages/filterparser) - TypeScript library for parsing data filter expressions using parsimmon [](https://www.npmjs.com/package/dbgate-filterparser)
|
||||
* [sqltree](https://github.com/dbgate/dbgate/tree/master/packages/sqltree) - JSON representation of SQL query, functions converting to SQL (TypeScript) [](https://www.npmjs.com/package/dbgate-sqltree)
|
||||
* [types](https://github.com/dbgate/dbgate/tree/master/packages/types) - common TypeScript definitions [](https://www.npmjs.com/package/dbgate-types)
|
||||
* [web](https://github.com/dbgate/dbgate/tree/master/packages/web) - frontend in Svelte (JavaScript) [](https://www.npmjs.com/package/dbgate-web)
|
||||
* [tools](https://github.com/dbgate/dbgate/tree/master/packages/tools) - various tools [](https://www.npmjs.com/package/dbgate-tools)
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XML_DIZ_INFO>
|
||||
<MASTER_PAD_VERSION_INFO>
|
||||
<MASTER_PAD_VERSION>4.0</MASTER_PAD_VERSION>
|
||||
<MASTER_PAD_EDITOR>AppVisor 1.0.43</MASTER_PAD_EDITOR>
|
||||
<MASTER_PAD_EDITOR_URL>http://publisher.appvisor.com</MASTER_PAD_EDITOR_URL>
|
||||
<MASTER_PAD_INFO>Portable Application Description, or PAD(TM) 2004 Association of Software Professionals (ASP)http://www.asp-shareware.org/pad is a data set standard and specification that is commercialsoftware publishers to disseminate certified information about their software application. The PAD format is copyright of the ASP and cannot be used without the formal licensing and permission of both the Association of Software Professionals and its agent AppVisor.com.</MASTER_PAD_INFO>
|
||||
<CERTIFIED>No</CERTIFIED>
|
||||
<CERTIFICATE_ID></CERTIFICATE_ID>
|
||||
<CERTIFICATE_LICENSE></CERTIFICATE_LICENSE>
|
||||
</MASTER_PAD_VERSION_INFO>
|
||||
<RoboSoft>
|
||||
<Company_UIN>CMP-9600EC03382B</Company_UIN>
|
||||
<Product_UIN>APP-A60089A1482B</Product_UIN>
|
||||
<Publish_on_CD>N</Publish_on_CD>
|
||||
<NewsFeed_Search_String></NewsFeed_Search_String>
|
||||
<Search_String>DbGate</Search_String>
|
||||
<Press_Release_Search_String></Press_Release_Search_String>
|
||||
<Backlink></Backlink>
|
||||
<Comments_For_Reviewer></Comments_For_Reviewer>
|
||||
<Search_String_Unique>DbGate</Search_String_Unique>
|
||||
<Search_Engine_Search_String></Search_Engine_Search_String>
|
||||
<Web_Directories_Search_String></Web_Directories_Search_String>
|
||||
<Company_Description></Company_Description>
|
||||
</RoboSoft>
|
||||
<Company_Info>
|
||||
<PublisherID>pid-86000673282b</PublisherID>
|
||||
<BrandID>bid-9600ec03382b</BrandID>
|
||||
<Company_Name>Jan Prochazka</Company_Name>
|
||||
<Country>Czech Republic</Country>
|
||||
<State_Province>Czechia</State_Province>
|
||||
<City_Town>Prague</City_Town>
|
||||
<Zip_Postal_Code>18100</Zip_Postal_Code>
|
||||
<Address_1>Vratislavska 396</Address_1>
|
||||
<Address_2>Praha 8</Address_2>
|
||||
<Company_WebSite_URL>https://dbgate.org/</Company_WebSite_URL>
|
||||
<Contact_Info>
|
||||
<Contact_First_Name>Jan</Contact_First_Name>
|
||||
<Contact_Last_Name>Prochazka</Contact_Last_Name>
|
||||
<Contact_Email>jenasoft.database@gmail.com</Contact_Email>
|
||||
<Author_First_Name>Jan</Author_First_Name>
|
||||
<Author_Last_Name>Prochazka</Author_Last_Name>
|
||||
<Author_Email>jenasoft.database@gmail.com</Author_Email>
|
||||
<Contact_Phone></Contact_Phone>
|
||||
</Contact_Info>
|
||||
<Support_Info>
|
||||
<Sales_Email>jenasoft.database@gmail.com</Sales_Email>
|
||||
<Support_Email>jenasoft.database@gmail.com</Support_Email>
|
||||
<General_Email>jenasoft.database@gmail.com</General_Email>
|
||||
<Sales_Phone></Sales_Phone>
|
||||
<Support_Phone></Support_Phone>
|
||||
<General_Phone></General_Phone>
|
||||
<Fax_Phone></Fax_Phone>
|
||||
</Support_Info>
|
||||
<LinkedinPage></LinkedinPage>
|
||||
<TwitterCompanyPage></TwitterCompanyPage>
|
||||
<FacebookCompanyPage></FacebookCompanyPage>
|
||||
</Company_Info>
|
||||
<Program_Info>
|
||||
<AppID>app-a60089a1482b</AppID>
|
||||
<Program_Name>DbGate</Program_Name>
|
||||
<Program_Version>3.8.40</Program_Version>
|
||||
<Program_Release_Month>01</Program_Release_Month>
|
||||
<Program_Release_Day>21</Program_Release_Day>
|
||||
<Program_Release_Year>2021</Program_Release_Year>
|
||||
<Program_Cost_Dollars>0</Program_Cost_Dollars>
|
||||
<Program_Type>Freeware</Program_Type>
|
||||
<Program_Category_Class>Business::Databases & Tools</Program_Category_Class>
|
||||
<Program_Specific_Category></Program_Specific_Category>
|
||||
<Program_Categories>Business & Finance :: Database Managers,Programming :: SQL,Programming :: Databases & Networks</Program_Categories>
|
||||
<Program_Release_Status>New Release</Program_Release_Status>
|
||||
<Program_Install_Support>Install and Uninstall</Program_Install_Support>
|
||||
<Program_Target_Platform>Windows</Program_Target_Platform>
|
||||
<Program_OS_Support>Win2000,WinXP,Win7 x32,Win7 x64,Windows 8,Windows 10,WinServer,WinOther,Windows2000,Windows2003,Windows Server 2000,Windows Server 2003,Windows Server 2008,Windows Server 2008r2,Windows Server 2012,Windows Tablet PC Edition 2005,Windows Media Center Edition 2005,WinVista,Windows Vista Starter,Windows Vista Home Basic,Windows Vista Home Premium,Windows Vista Business,Windows Vista Enterprise,Windows Vista Ultimate,WinVista x64,Windows Vista Home Basic x64,Windows Vista Home Premium x64,Windows Vista Business x64,Windows Vista Enterprise x64,Windows Vista Ultimate x64</Program_OS_Support>
|
||||
<Program_Language>English</Program_Language>
|
||||
<Program_Change_Info>Form view - for tables with may columns
|
||||
Windows authentization for SQL Server
|
||||
Support for MariaDB</Program_Change_Info>
|
||||
<Program_System_Requirements>2GB RAM, 100MB disk space</Program_System_Requirements>
|
||||
<Limitations>No limitations</Limitations>
|
||||
<Awards></Awards>
|
||||
<File_Info>
|
||||
<File_Size_Bytes>60973056</File_Size_Bytes>
|
||||
<File_Size_K>59544</File_Size_K>
|
||||
<File_Size_MB>58.15</File_Size_MB>
|
||||
</File_Info>
|
||||
<Expire_Info>
|
||||
<Has_Expire_Info>N</Has_Expire_Info>
|
||||
<Expire_Count></Expire_Count>
|
||||
<Expire_Based_On></Expire_Based_On>
|
||||
<Expire_Year></Expire_Year>
|
||||
<Expire_Other_Info></Expire_Other_Info>
|
||||
<Expire_Month></Expire_Month>
|
||||
<Expire_Day></Expire_Day>
|
||||
</Expire_Info>
|
||||
<Includes_JAVA_VM></Includes_JAVA_VM>
|
||||
<Includes_DirectX></Includes_DirectX>
|
||||
<Includes_VB_Runtime></Includes_VB_Runtime>
|
||||
</Program_Info>
|
||||
<Web_Info>
|
||||
<Application_URLs>
|
||||
<Application_Info_URL>https://dbgate.org/</Application_Info_URL>
|
||||
<Application_Order_URL></Application_Order_URL>
|
||||
<Application_Screenshot_URL>https://raw.githubusercontent.com/dbshell/dbgate/master/screenshot800.png</Application_Screenshot_URL>
|
||||
<Application_Icon_URL>https://raw.githubusercontent.com/dbshell/dbgate/master/app/icon32.png</Application_Icon_URL>
|
||||
<Application_XML_File_URL>http://repository.appvisor.com/app-a60089a1482b/site-01/DbGate_pad.xml</Application_XML_File_URL>
|
||||
</Application_URLs>
|
||||
<Download_URLs>
|
||||
<Primary_Download_URL>https://github.com/dbshell/dbgate/releases/latest/download/dbgate-latest.exe</Primary_Download_URL>
|
||||
<Secondary_Download_URL></Secondary_Download_URL>
|
||||
<Additional_Download_URL_1></Additional_Download_URL_1>
|
||||
<Additional_Download_URL_2></Additional_Download_URL_2>
|
||||
</Download_URLs>
|
||||
</Web_Info>
|
||||
<Program_Descriptions>
|
||||
<English>
|
||||
<Keywords>sql, management, tool, microsoft, sql, mssql, postgres, postgresql, mysql, csv, data, export, import, javascript, database</Keywords>
|
||||
<Char_Desc_45>Modern data manipulation tool</Char_Desc_45>
|
||||
<Char_Desc_80>Modern cross-platform data manipulation tool</Char_Desc_80>
|
||||
<Char_Desc_250>Modern cross-platform data manipulation tool for Microsoft SQL Server, PostgreSQL and MySQL. Query designer. Master/detail views. Advanced data filtering. Export, import. Exports can be saved as jobs</Char_Desc_250>
|
||||
<Char_Desc_450>Modern cross-platform data manipulation tool for Microsoft SQL Server, PostgreSQL and MySQL. Query designer. Charts. Master/detail views. Advanced data filtering. Export, import. Exports can be saved as jobs</Char_Desc_450>
|
||||
<Char_Desc_2000>Modern cross-platform data manipulation tool for Microsoft SQL Server, PostgreSQL and MySQL
|
||||
Main features:
|
||||
Quick database browser
|
||||
Database editor, view and and views, stored procedures, functions, triggers, table relations
|
||||
Export, import - CSV, MS Excel
|
||||
Form view for tables with many columns
|
||||
Query designer
|
||||
Advanced data filtering
|
||||
JavaScript data scripting
|
||||
Run queries, code completion
|
||||
Display data from referenced tables
|
||||
Create charts, supports for active charts - are refreshed when underlying source is changed</Char_Desc_2000>
|
||||
</English>
|
||||
<Czech>
|
||||
<Keywords>sql, management, tool, microsoft, sql, mssql, postgres, postgresql, mysql, csv, data, export, import, javascript, database</Keywords>
|
||||
<Char_Desc_45>Moderní správce databází</Char_Desc_45>
|
||||
<Char_Desc_80>Správce databází MS SQL Server, MySQL a PostgreSQL</Char_Desc_80>
|
||||
<Char_Desc_250>Nástroj pro správu databází MS SQL Server, MySQL a PostgreSQL</Char_Desc_250>
|
||||
<Char_Desc_450>Nástroj pro správu databází MS SQL Server, MySQL a PostgreSQL</Char_Desc_450>
|
||||
<Char_Desc_2000>Moderní multiplatformní nástroj pro práci s databázemi pro Microsoft SQL Server, PostgreSQL a MySQL.
|
||||
Hlavní funkce:
|
||||
Prohlížení databáze, tabulek, pohledů, uložených procedur a funkcí
|
||||
Export, import - CSV, MS Excel
|
||||
Zobrazení jednoho řádku tabulky "na výšku" - vhodné pro tabulky s mnoha sloupci
|
||||
Grafický návrh dotazů
|
||||
Pokročilé filtrování dat
|
||||
Advanced data filtering
|
||||
Skriptování pomocí jazyka JavaScript
|
||||
SQL editor, doplňování kódu
|
||||
Zobrazování referencových dat, na základě cizích klíčů
|
||||
Vytváření grafů, podpora aktivních grafů - obnoví se, pokud se změní zdroj dat</Char_Desc_2000>
|
||||
</Czech>
|
||||
</Program_Descriptions>
|
||||
<Permissions>
|
||||
<Distribution_Permissions>This product can be freely distributed without limitations.</Distribution_Permissions>
|
||||
<EULA>MIT License
|
||||
|
||||
Copyright (c) 2021 Jan Prochazka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.</EULA>
|
||||
</Permissions>
|
||||
<Affiliates>
|
||||
<Affiliates_Regsoft_Order_Page></Affiliates_Regsoft_Order_Page>
|
||||
<Affiliates_Regsoft_Vendor_ID></Affiliates_Regsoft_Vendor_ID>
|
||||
<Affiliates_Regsoft_Product_ID></Affiliates_Regsoft_Product_ID>
|
||||
<Affiliates_FORM></Affiliates_FORM>
|
||||
<Affiliates_FORM_VER></Affiliates_FORM_VER>
|
||||
<Affiliates_VERSION></Affiliates_VERSION>
|
||||
<Affiliates_URL></Affiliates_URL>
|
||||
<Affiliates_Information_Page></Affiliates_Information_Page>
|
||||
<Affiliates_Avangate_Order_Page></Affiliates_Avangate_Order_Page>
|
||||
<Affiliates_Avangate_Vendor_ID></Affiliates_Avangate_Vendor_ID>
|
||||
<Affiliates_Avangate_Product_ID></Affiliates_Avangate_Product_ID>
|
||||
<Affiliates_Avangate_Maximum_Commission_Rate></Affiliates_Avangate_Maximum_Commission_Rate>
|
||||
<Affiliates_BlueSnap_Order_Page></Affiliates_BlueSnap_Order_Page>
|
||||
<Affiliates_BlueSnap_Vendor_ID></Affiliates_BlueSnap_Vendor_ID>
|
||||
<Affiliates_BlueSnap_Product_ID></Affiliates_BlueSnap_Product_ID>
|
||||
<Affiliates_BlueSnap_Maximum_Commission_Rate></Affiliates_BlueSnap_Maximum_Commission_Rate>
|
||||
<Affiliates_BMTMicro_Order_Page></Affiliates_BMTMicro_Order_Page>
|
||||
<Affiliates_BMTMicro_Vendor_ID></Affiliates_BMTMicro_Vendor_ID>
|
||||
<Affiliates_BMTMicro_Product_ID></Affiliates_BMTMicro_Product_ID>
|
||||
<Affiliates_BMTMicro_Maximum_Commission_Rate></Affiliates_BMTMicro_Maximum_Commission_Rate>
|
||||
<Affiliates_eSellerate_Order_Page></Affiliates_eSellerate_Order_Page>
|
||||
<Affiliates_eSellerate_Vendor_ID></Affiliates_eSellerate_Vendor_ID>
|
||||
<Affiliates_eSellerate_Product_ID></Affiliates_eSellerate_Product_ID>
|
||||
<Affiliates_eSellerate_Maximum_Commission_Rate></Affiliates_eSellerate_Maximum_Commission_Rate>
|
||||
<Affiliates_Regnet_Order_Page></Affiliates_Regnet_Order_Page>
|
||||
<Affiliates_Regnet_Vendor_ID></Affiliates_Regnet_Vendor_ID>
|
||||
<Affiliates_Regnet_Product_ID></Affiliates_Regnet_Product_ID>
|
||||
<Affiliates_Regnet_Maximum_Commission_Rate></Affiliates_Regnet_Maximum_Commission_Rate>
|
||||
<Affiliates_Regsoft_Maximum_Commission_Rate></Affiliates_Regsoft_Maximum_Commission_Rate>
|
||||
<Affiliates_ShareIt_Order_Page></Affiliates_ShareIt_Order_Page>
|
||||
<Affiliates_ShareIt_Vendor_ID></Affiliates_ShareIt_Vendor_ID>
|
||||
<Affiliates_ShareIt_Product_ID></Affiliates_ShareIt_Product_ID>
|
||||
<Affiliates_ShareIt_Maximum_Commission_Rate></Affiliates_ShareIt_Maximum_Commission_Rate>
|
||||
</Affiliates>
|
||||
<NewsFeed>
|
||||
<NewsFeed_FORM></NewsFeed_FORM>
|
||||
<NewsFeed_VERSION></NewsFeed_VERSION>
|
||||
<NewsFeed_URL></NewsFeed_URL>
|
||||
<NewsFeed_DESCRIPTION></NewsFeed_DESCRIPTION>
|
||||
<NewsFeed_Feed_URL></NewsFeed_Feed_URL>
|
||||
<NewsFeed_Type></NewsFeed_Type>
|
||||
<NewsFeed_Site_URL></NewsFeed_Site_URL>
|
||||
<NewsFeed_Author_First_Name>Jan</NewsFeed_Author_First_Name>
|
||||
<NewsFeed_Author_Last_Name>Prochazka</NewsFeed_Author_Last_Name>
|
||||
<NewsFeed_Author_Email>jenasoft.database@gmail.com</NewsFeed_Author_Email>
|
||||
<NewsFeed_Language></NewsFeed_Language>
|
||||
<NewsFeed_Title></NewsFeed_Title>
|
||||
<NewsFeed_Description_70></NewsFeed_Description_70>
|
||||
<NewsFeed_Description_250></NewsFeed_Description_250>
|
||||
<NewsFeed_Purpose></NewsFeed_Purpose>
|
||||
<NewsFeed_Site_Name></NewsFeed_Site_Name>
|
||||
</NewsFeed>
|
||||
<Press_Release>
|
||||
<Press_Release></Press_Release>
|
||||
<Headline></Headline>
|
||||
<Summary></Summary>
|
||||
<Press_Release_Plain></Press_Release_Plain>
|
||||
</Press_Release>
|
||||
<Site>
|
||||
<Site_FORM></Site_FORM>
|
||||
<Site_VERSION></Site_VERSION>
|
||||
<Site_DESCRIPTION></Site_DESCRIPTION>
|
||||
<Site_Site_Title>DbGate github</Site_Site_Title>
|
||||
<Site_Site_URL>https://github.com/dbshell/dbgate</Site_Site_URL>
|
||||
<Site_Keywords>database, manager, organizer, explorer, mssql, sqlserver, tsql, sql, mysql, postgre, postgresql, export, import, filter</Site_Keywords>
|
||||
<Site_Description_100>DbGate developer site</Site_Description_100>
|
||||
<Site_Description_250>Source code for DbGate. Create issues, porticipate, post pull requests. Explore, how to write plugins for DbGate</Site_Description_250>
|
||||
<Site_Description_450>Source code for DbGate. Create issues, porticipate, post pull requests. Explore, how to write plugins for DbGate</Site_Description_450>
|
||||
<Site_Contact_First_Name>Jan</Site_Contact_First_Name>
|
||||
<Site_Contact_Last_Name>Prochazka</Site_Contact_Last_Name>
|
||||
<Site_Contact_Email>jenasoft.database@gmail.com</Site_Contact_Email>
|
||||
</Site>
|
||||
<Dynamic_PAD>
|
||||
<General>
|
||||
<DP_Pad_Enabled>TRUE</DP_Pad_Enabled>
|
||||
<DP_AtFormFill_Enabled>TRUE</DP_AtFormFill_Enabled>
|
||||
<DP_Distributive_Enabled>TRUE</DP_Distributive_Enabled>
|
||||
<DP_Script_Base_URL>http://repository.appvisor.com/</DP_Script_Base_URL>
|
||||
<DP_Pad_Mask>DbGate_pad.xml</DP_Pad_Mask>
|
||||
<DP_Distributive_Primary_URL>https://github.com/dbshell/dbgate/releases/latest/download/dbgate-latest.exe</DP_Distributive_Primary_URL>
|
||||
<DP_Distributive_Mask>dbgate-latest.exe</DP_Distributive_Mask>
|
||||
</General>
|
||||
</Dynamic_PAD>
|
||||
</XML_DIZ_INFO>
|
||||
@@ -6,7 +6,7 @@
|
||||
<MASTER_PAD_INFO>Portable Application Description, or PAD for short, is a data set that is used by shareware authors to disseminate information to anyone interested in their software products. To find out more go to http://www.asp-shareware.org/pad</MASTER_PAD_INFO>
|
||||
</MASTER_PAD_VERSION_INFO>
|
||||
<Company_Info>
|
||||
<Company_Name>JenaSoft</Company_Name>
|
||||
<Company_Name>Jan Prochazka</Company_Name>
|
||||
<Address_1>Vratislavska 396</Address_1>
|
||||
<Address_2>Praha 8</Address_2>
|
||||
<City_Town>Prague</City_Town>
|
||||
@@ -139,17 +139,34 @@ SOFTWARE.</EULA>
|
||||
<Char_Desc_2000>
|
||||
Modern cross-platform data manipulation tool for Microsoft SQL Server, PostgreSQL and MySQL
|
||||
Main features:
|
||||
Quick database browser.
|
||||
Database editor, view and and views, stored procedures, functions, triggers, table relations.
|
||||
Quick database browser
|
||||
Database editor, view and and views, stored procedures, functions, triggers, table relations
|
||||
Export, import - CSV, MS Excel
|
||||
Form view for tables with many columns
|
||||
Query designer
|
||||
Advanced data filtering.
|
||||
Export, import.
|
||||
JavaScript data scripting.
|
||||
Run queries, code completion.
|
||||
Display data from referenced tables.
|
||||
Advanced data filtering
|
||||
JavaScript data scripting
|
||||
Run queries, code completion
|
||||
Display data from referenced tables
|
||||
Create charts, supports for active charts - are refreshed when underlying source is changed
|
||||
</Char_Desc_2000>
|
||||
</English>
|
||||
<Czech>
|
||||
<Char_Desc_250>Správce databází MS SQL Server, MySQL a PostgreSQL</Char_Desc_250>
|
||||
<Char_Desc_2000>
|
||||
Moderní multiplatformní nástroj pro práci s databázemi pro Microsoft SQL Server, PostgreSQL a MySQL.
|
||||
Hlavní funkce:
|
||||
Prohlížení databáze, tabulek, pohledů, uložených procedur a funkcí
|
||||
Export, import - CSV, MS Excel
|
||||
Zobrazení jednoho řádku tabulky "na výšku" - vhodné pro tabulky s mnoha sloupci
|
||||
Grafický návrh dotazů
|
||||
Pokročilé filtrování dat
|
||||
Advanced data filtering
|
||||
Skriptování pomocí jazyka JavaScript
|
||||
SQL editor, doplňování kódu
|
||||
Zobrazování referencových dat, na základě cizích klíčů
|
||||
Vytváření grafů, podpora aktivních grafů - obnoví se, pokud se změní zdroj dat
|
||||
</Char_Desc_2000>
|
||||
</Czech>
|
||||
</Program_Descriptions>
|
||||
</XML_DIZ_INFO>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
+24
-9
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"name": "dbgate",
|
||||
"version": "3.8.28",
|
||||
"version": "4.0.0",
|
||||
"private": true,
|
||||
"author": "Jan Prochazka <jenasoft.database@gmail.com>",
|
||||
"description": "Opensource database administration tool",
|
||||
"dependencies": {
|
||||
"electron-log": "^4.3.1",
|
||||
"electron-store": "^5.1.1",
|
||||
@@ -10,7 +11,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbshell/dbgate.git"
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"build": {
|
||||
"appId": "org.dbgate",
|
||||
@@ -25,7 +26,8 @@
|
||||
"linux": {
|
||||
"target": [
|
||||
"AppImage",
|
||||
"deb"
|
||||
"deb",
|
||||
"snap"
|
||||
],
|
||||
"icon": "icon.png",
|
||||
"artifactName": "dbgate-linux-${version}.${ext}",
|
||||
@@ -35,6 +37,15 @@
|
||||
"github"
|
||||
]
|
||||
},
|
||||
"snap": {
|
||||
"publish": [
|
||||
"github",
|
||||
"snapStore"
|
||||
],
|
||||
"environment": {
|
||||
"ELECTRON_SNAP": "true"
|
||||
}
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
@@ -43,7 +54,8 @@
|
||||
"icon": "icon.ico",
|
||||
"publish": [
|
||||
"github"
|
||||
]
|
||||
],
|
||||
"rfc3161TimeStampServer": "http://sha256timestamp.ws.symantec.com/sha256/timestamp"
|
||||
},
|
||||
"files": [
|
||||
"packages",
|
||||
@@ -56,16 +68,19 @@
|
||||
"start": "cross-env ELECTRON_START_URL=http://localhost:5000 electron .",
|
||||
"start:local": "cross-env electron .",
|
||||
"dist": "electron-builder",
|
||||
"build": "cd ../packages/api && yarn build && cd ../web && yarn build:app && cd ../../app && yarn dist",
|
||||
"build:local": "cd ../packages/api && yarn build && cd ../web && yarn build:app && cd ../../app && yarn predist",
|
||||
"build": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn dist",
|
||||
"build:local": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn predist",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"predist": "copyfiles ../packages/api/dist/* packages && copyfiles \"../packages/web/build/*\" packages && copyfiles \"../packages/web/build/**/*\" packages"
|
||||
"predist": "copyfiles ../packages/api/dist/* packages && copyfiles \"../packages/web/public/*\" packages && copyfiles \"../packages/web/public/**/*\" packages"
|
||||
},
|
||||
"main": "src/electron.js",
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.2.0",
|
||||
"cross-env": "^6.0.3",
|
||||
"electron": "11.1.1",
|
||||
"electron": "11.2.3",
|
||||
"electron-builder": "22.9.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"msnodesqlv8": "^2.0.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
-36
@@ -1,6 +1,6 @@
|
||||
const electron = require('electron');
|
||||
const os = require('os');
|
||||
const { Menu } = require('electron');
|
||||
const { Menu, ipcMain } = require('electron');
|
||||
const { fork } = require('child_process');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
const Store = require('electron-store');
|
||||
@@ -20,9 +20,14 @@ const store = new Store();
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
let splashWindow;
|
||||
let mainMenu;
|
||||
|
||||
log.transports.file.level = 'debug';
|
||||
autoUpdater.logger = log;
|
||||
// TODO - create settings for this
|
||||
// appUpdater.channel = 'beta';
|
||||
|
||||
let commands = {};
|
||||
|
||||
function hideSplash() {
|
||||
if (splashWindow) {
|
||||
@@ -32,29 +37,48 @@ function hideSplash() {
|
||||
mainWindow.show();
|
||||
}
|
||||
|
||||
function commandItem(id) {
|
||||
const command = commands[id];
|
||||
return {
|
||||
id,
|
||||
label: command ? command.menuName || command.toolbarName || command.name : id,
|
||||
accelerator: command ? command.keyText : undefined,
|
||||
enabled: command ? command.enabled : false,
|
||||
click() {
|
||||
mainWindow.webContents.executeJavaScript(`dbgate_runCommand('${id}')`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
const template = [
|
||||
{
|
||||
label: 'File',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Connect to database',
|
||||
click() {
|
||||
mainWindow.webContents.executeJavaScript(`dbgate_createNewConnection()`);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'New query',
|
||||
click() {
|
||||
mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
|
||||
},
|
||||
},
|
||||
commandItem('new.connection'),
|
||||
commandItem('file.open'),
|
||||
commandItem('group.save'),
|
||||
commandItem('group.saveAs'),
|
||||
{ type: 'separator' },
|
||||
{ role: 'close' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [{ role: 'copy' }, { role: 'paste' }],
|
||||
label: 'Window',
|
||||
submenu: [commandItem('new.query'), { type: 'separator' }, commandItem('tabs.closeAll'), { role: 'minimize' }],
|
||||
},
|
||||
|
||||
// {
|
||||
// label: 'Edit',
|
||||
// submenu: [
|
||||
// { role: 'undo' },
|
||||
// { role: 'redo' },
|
||||
// { type: 'separator' },
|
||||
// { role: 'cut' },
|
||||
// { role: 'copy' },
|
||||
// { role: 'paste' },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
@@ -67,20 +91,7 @@ function buildMenu() {
|
||||
{ role: 'zoomout' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'window',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Close all tabs',
|
||||
click() {
|
||||
mainWindow.webContents.executeJavaScript('dbgate_closeAll()');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'minimize' },
|
||||
{ role: 'close' },
|
||||
commandItem('theme.changeTheme'),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -95,7 +106,7 @@ function buildMenu() {
|
||||
{
|
||||
label: 'DbGate on GitHub',
|
||||
click() {
|
||||
require('electron').shell.openExternal('https://github.com/dbshell/dbgate');
|
||||
require('electron').shell.openExternal('https://github.com/dbgate/dbgate');
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -105,11 +116,12 @@ function buildMenu() {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'About',
|
||||
label: 'Report problem or feature request',
|
||||
click() {
|
||||
mainWindow.webContents.executeJavaScript(`dbgate_showAbout()`);
|
||||
require('electron').shell.openExternal('https://github.com/dbgate/dbgate/issues/new');
|
||||
},
|
||||
},
|
||||
commandItem('about.show'),
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -117,6 +129,24 @@ function buildMenu() {
|
||||
return Menu.buildFromTemplate(template);
|
||||
}
|
||||
|
||||
ipcMain.on('update-commands', async (event, arg) => {
|
||||
commands = JSON.parse(arg);
|
||||
for (const key of Object.keys(commands)) {
|
||||
const menu = mainMenu.getMenuItemById(key);
|
||||
if (!menu) continue;
|
||||
const command = commands[key];
|
||||
|
||||
// rebuild menu
|
||||
if (menu.label != command.text || menu.accelerator != command.keyText) {
|
||||
mainMenu = buildMenu();
|
||||
mainWindow.setMenu(mainMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
menu.enabled = command.enabled;
|
||||
}
|
||||
});
|
||||
|
||||
function createWindow() {
|
||||
const bounds = store.get('winBounds');
|
||||
|
||||
@@ -133,13 +163,14 @@ function createWindow() {
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.setMenu(buildMenu());
|
||||
mainMenu = buildMenu();
|
||||
mainWindow.setMenu(mainMenu);
|
||||
|
||||
function loadMainWindow() {
|
||||
const startUrl =
|
||||
process.env.ELECTRON_START_URL ||
|
||||
url.format({
|
||||
pathname: path.join(__dirname, '../packages/web/build/index.html'),
|
||||
pathname: path.join(__dirname, '../packages/web/public/index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
});
|
||||
@@ -172,8 +203,13 @@ function createWindow() {
|
||||
if (process.env.ELECTRON_START_URL) {
|
||||
loadMainWindow();
|
||||
} else {
|
||||
const apiProcess = fork(path.join(__dirname, '../packages/api/dist/bundle.js'), ['--dynport']);
|
||||
apiProcess.on('message', (msg) => {
|
||||
const apiProcess = fork(path.join(__dirname, '../packages/api/dist/bundle.js'), [
|
||||
'--dynport',
|
||||
'--native-modules',
|
||||
path.join(__dirname, 'nativeModules'),
|
||||
// '../../../src/nativeModules'
|
||||
]);
|
||||
apiProcess.on('message', msg => {
|
||||
if (msg.msgtype == 'listening') {
|
||||
const { port } = msg;
|
||||
global['port'] = port;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
const content = require('./nativeModulesContent');
|
||||
|
||||
module.exports = content;
|
||||
+327
-12
@@ -119,6 +119,16 @@ ansi-align@^3.0.0:
|
||||
dependencies:
|
||||
string-width "^3.0.0"
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||
|
||||
ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
@@ -177,6 +187,19 @@ app-builder-lib@22.9.1:
|
||||
semver "^7.3.2"
|
||||
temp-file "^3.3.7"
|
||||
|
||||
aproba@^1.0.3:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
|
||||
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
||||
|
||||
are-we-there-yet@~1.1.2:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
||||
integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
|
||||
dependencies:
|
||||
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"
|
||||
@@ -204,6 +227,20 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
bl@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489"
|
||||
integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
bluebird-lst@^1.0.9:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c"
|
||||
@@ -248,6 +285,14 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
builder-util-runtime@8.7.2:
|
||||
version "8.7.2"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.2.tgz#d93afc71428a12789b437e13850e1fa7da956d72"
|
||||
@@ -319,6 +364,11 @@ chalk@^4.1.0:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
chromium-pickle-js@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205"
|
||||
@@ -359,6 +409,11 @@ clone-response@^1.0.2:
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
code-point-at@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
@@ -434,6 +489,11 @@ configstore@^5.0.1:
|
||||
write-file-atomic "^3.0.0"
|
||||
xdg-basedir "^4.0.0"
|
||||
|
||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
|
||||
|
||||
copyfiles@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.2.0.tgz#d9fc6c06f299337fb7eeb7ea5887e9d7188d9d47"
|
||||
@@ -517,6 +577,13 @@ decompress-response@^3.3.0:
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
decompress-response@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
|
||||
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
|
||||
deep-extend@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
|
||||
@@ -534,6 +601,16 @@ define-properties@^1.1.3:
|
||||
dependencies:
|
||||
object-keys "^1.0.12"
|
||||
|
||||
delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
||||
|
||||
detect-libc@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
|
||||
|
||||
detect-node@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
|
||||
@@ -640,10 +717,10 @@ electron-updater@^4.3.5:
|
||||
lodash.isequal "^4.5.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
electron@11.1.1:
|
||||
version "11.1.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-11.1.1.tgz#188f036f8282798398dca9513e9bb3b10213e3aa"
|
||||
integrity sha512-tlbex3xosJgfileN6BAQRotevPRXB/wQIq48QeQ08tUJJrXwE72c8smsM/hbHx5eDgnbfJ2G3a60PmRjHU2NhA==
|
||||
electron@11.2.3:
|
||||
version "11.2.3"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-11.2.3.tgz#8ad1d9858436cfca0e2e5ea7fea326794ae58ebb"
|
||||
integrity sha512-6yxOc42nDAptHKNlUG/vcOh2GI9x2fqp2nQbZO0/3sz2CrwsJkwR3i3oMN9XhVJaqI7GK1vSCJz0verOkWlXcQ==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^12.0.12"
|
||||
@@ -664,7 +741,7 @@ encodeurl@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
@@ -706,6 +783,11 @@ esprima@^4.0.0:
|
||||
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"
|
||||
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
|
||||
|
||||
extract-zip@^1.0.3:
|
||||
version "1.6.7"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
|
||||
@@ -747,6 +829,11 @@ find-up@^3.0.0:
|
||||
dependencies:
|
||||
locate-path "^3.0.0"
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
@@ -771,6 +858,20 @@ fs.realpath@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
|
||||
dependencies:
|
||||
aproba "^1.0.3"
|
||||
console-control-strings "^1.0.0"
|
||||
has-unicode "^2.0.0"
|
||||
object-assign "^4.1.0"
|
||||
signal-exit "^3.0.0"
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.1"
|
||||
wide-align "^1.1.0"
|
||||
|
||||
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
@@ -790,6 +891,11 @@ get-stream@^5.1.0:
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
github-from-package@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
||||
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
|
||||
|
||||
glob@^7.0.5:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
@@ -871,6 +977,11 @@ has-flag@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
has-unicode@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
|
||||
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
|
||||
|
||||
has-yarn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
|
||||
@@ -900,6 +1011,11 @@ iconv-lite@^0.6.2:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
ieee754@^1.1.13:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
import-lazy@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
|
||||
@@ -918,7 +1034,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -935,6 +1051,13 @@ is-ci@^2.0.0:
|
||||
dependencies:
|
||||
ci-info "^2.0.0"
|
||||
|
||||
is-fullwidth-code-point@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
||||
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
|
||||
dependencies:
|
||||
number-is-nan "^1.0.0"
|
||||
|
||||
is-fullwidth-code-point@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
@@ -1150,6 +1273,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
mimic-response@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
|
||||
minimatch@^3.0.3, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@@ -1162,11 +1290,16 @@ minimist@0.0.8:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
mkdirp@0.5.1, mkdirp@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
@@ -1184,6 +1317,31 @@ ms@2.1.2, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
msnodesqlv8@^2.0.10:
|
||||
version "2.0.10"
|
||||
resolved "https://registry.yarnpkg.com/msnodesqlv8/-/msnodesqlv8-2.0.10.tgz#47d05157c2d30e5f48fa0aa550a0353ab56a027c"
|
||||
integrity sha512-oA46Y8ZQxYEW9M8U+Wg4bI5g8nI2pueIkyameEpxanHqr9d5w4a0Pgk2WvugHiY1UYmN1bPziUdqBgwkBSGrnA==
|
||||
dependencies:
|
||||
nan "^2.14.2"
|
||||
prebuild-install "^6.0.0"
|
||||
|
||||
nan@^2.14.2:
|
||||
version "2.14.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
napi-build-utils@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
|
||||
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
|
||||
|
||||
node-abi@^2.7.0:
|
||||
version "2.19.3"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d"
|
||||
integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg==
|
||||
dependencies:
|
||||
semver "^5.4.1"
|
||||
|
||||
noms@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
||||
@@ -1192,6 +1350,11 @@ noms@0.0.0:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "~1.0.31"
|
||||
|
||||
noop-logger@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
|
||||
integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=
|
||||
|
||||
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"
|
||||
@@ -1215,6 +1378,26 @@ npm-conf@^1.1.3:
|
||||
config-chain "^1.1.11"
|
||||
pify "^3.0.0"
|
||||
|
||||
npmlog@^4.0.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
|
||||
dependencies:
|
||||
are-we-there-yet "~1.1.2"
|
||||
console-control-strings "~1.1.0"
|
||||
gauge "~2.7.3"
|
||||
set-blocking "~2.0.0"
|
||||
|
||||
number-is-nan@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
|
||||
|
||||
object-assign@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-keys@^1.0.12:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
@@ -1305,6 +1488,27 @@ pkg-up@^3.0.1:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
prebuild-install@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316"
|
||||
integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
expand-template "^2.0.3"
|
||||
github-from-package "0.0.0"
|
||||
minimist "^1.2.3"
|
||||
mkdirp-classic "^0.5.3"
|
||||
napi-build-utils "^1.0.1"
|
||||
node-abi "^2.7.0"
|
||||
noop-logger "^0.1.1"
|
||||
npmlog "^4.0.1"
|
||||
pump "^3.0.0"
|
||||
rc "^1.2.7"
|
||||
simple-get "^3.0.3"
|
||||
tar-fs "^2.0.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
which-pm-runs "^1.0.0"
|
||||
|
||||
prepend-http@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
@@ -1345,7 +1549,7 @@ pupa@^2.0.1:
|
||||
dependencies:
|
||||
escape-goat "^2.0.0"
|
||||
|
||||
rc@^1.2.8:
|
||||
rc@^1.2.7, rc@^1.2.8:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
|
||||
@@ -1366,7 +1570,7 @@ read-config-file@6.0.0:
|
||||
json5 "^2.1.2"
|
||||
lazy-val "^1.0.4"
|
||||
|
||||
readable-stream@^2.2.2, readable-stream@~2.3.6:
|
||||
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"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@@ -1379,6 +1583,15 @@ readable-stream@^2.2.2, readable-stream@~2.3.6:
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.1.1, readable-stream@^3.4.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
readable-stream@~1.0.31:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||
@@ -1439,6 +1652,11 @@ roarr@^2.15.2:
|
||||
semver-compare "^1.0.0"
|
||||
sprintf-js "^1.1.2"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
@@ -1473,7 +1691,7 @@ semver-diff@^3.1.1:
|
||||
dependencies:
|
||||
semver "^6.3.0"
|
||||
|
||||
"semver@2 || 3 || 4 || 5":
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.4.1:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
@@ -1502,7 +1720,7 @@ serialize-error@^5.0.0:
|
||||
dependencies:
|
||||
type-fest "^0.8.0"
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
@@ -1519,11 +1737,30 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
signal-exit@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||
|
||||
simple-get@^3.0.3:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3"
|
||||
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
|
||||
dependencies:
|
||||
decompress-response "^4.2.0"
|
||||
once "^1.3.1"
|
||||
simple-concat "^1.0.0"
|
||||
|
||||
source-map-support@^0.5.19:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
@@ -1578,6 +1815,23 @@ stat-mode@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465"
|
||||
integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==
|
||||
|
||||
string-width@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
|
||||
dependencies:
|
||||
code-point-at "^1.0.0"
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
"string-width@^1.0.2 || 2":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string-width@^3.0.0, string-width@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
@@ -1596,6 +1850,13 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
string_decoder@~0.10.x:
|
||||
version "0.10.31"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
||||
@@ -1608,6 +1869,20 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
strip-ansi@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
@@ -1648,6 +1923,27 @@ supports-color@^7.1.0:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
tar-fs@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp-classic "^0.5.2"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.1.4"
|
||||
|
||||
tar-stream@^2.1.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
dependencies:
|
||||
bl "^4.0.3"
|
||||
end-of-stream "^1.4.1"
|
||||
fs-constants "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
temp-file@^3.3.7:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.7.tgz#686885d635f872748e384e871855958470aeb18a"
|
||||
@@ -1681,6 +1977,13 @@ truncate-utf8-bytes@^1.0.0:
|
||||
dependencies:
|
||||
utf8-byte-length "^1.0.1"
|
||||
|
||||
tunnel-agent@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
||||
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tunnel@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
@@ -1763,7 +2066,7 @@ utf8-byte-length@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
|
||||
integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
@@ -1781,6 +2084,11 @@ which-module@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||
|
||||
which-pm-runs@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
||||
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
@@ -1788,6 +2096,13 @@ which@^2.0.1:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wide-align@^1.1.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
||||
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2"
|
||||
|
||||
widest-line@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
const fs = require('fs');
|
||||
|
||||
let fillContent = '';
|
||||
|
||||
// if (!process.argv.includes('--electron')) {
|
||||
if (process.platform == 'win32') {
|
||||
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');`;
|
||||
}
|
||||
|
||||
const getContent = (empty) => `
|
||||
// this file is generated automatically by script fillNativeModules.js, do not edit it manually
|
||||
const content = {};
|
||||
|
||||
${empty ? '' : fillContent}
|
||||
|
||||
module.exports = content;
|
||||
`;
|
||||
|
||||
fs.writeFileSync(
|
||||
'packages/api/src/nativeModulesContent.js',
|
||||
getContent(process.argv.includes('--electron') ? true : false)
|
||||
);
|
||||
fs.writeFileSync('app/src/nativeModulesContent.js', getContent(false));
|
||||
+10
-7
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "4.0.3",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -8,7 +9,7 @@
|
||||
"start:api": "yarn workspace dbgate-api start",
|
||||
"start:api:portal": "yarn workspace dbgate-api start:portal",
|
||||
"start:api:covid": "yarn workspace dbgate-api start:covid",
|
||||
"start:web": "yarn workspace dbgate-web start",
|
||||
"start:web": "yarn workspace dbgate-web dev",
|
||||
"start:sqltree": "yarn workspace dbgate-sqltree start",
|
||||
"start:tools": "yarn workspace dbgate-tools start",
|
||||
"start:datalib": "yarn workspace dbgate-datalib start",
|
||||
@@ -20,22 +21,23 @@
|
||||
"build:lib": "yarn build:tools && yarn build:sqltree && yarn build:filterparser && yarn build:datalib",
|
||||
"build:app": "cd app && yarn install && yarn build",
|
||||
"build:api": "yarn workspace dbgate-api build",
|
||||
"build:web:docker": "yarn workspace dbgate-web build:docker",
|
||||
"build:web:docker": "yarn workspace dbgate-web build",
|
||||
"build:app:local": "cd app && yarn build:local",
|
||||
"start:app:local": "cd app && yarn start:local",
|
||||
"setCurrentVersion": "node setCurrentVersion",
|
||||
"generatePadFile": "node generatePadFile",
|
||||
|
||||
"copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/build/* docker -u 2 && copyfiles \"packages/web/build/**/*\" docker -u 2",
|
||||
"fillNativeModules": "node fillNativeModules",
|
||||
"fillNativeModulesElectron": "node fillNativeModules --eletron",
|
||||
"prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src",
|
||||
"copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/public/* docker -u 2 && copyfiles \"packages/web/public/**/*\" docker -u 2",
|
||||
"prepare:docker": "yarn build:web:docker && yarn build:api && yarn copy:docker:build",
|
||||
|
||||
"prepare": "yarn build:lib",
|
||||
"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\"",
|
||||
"ts:api": "yarn workspace dbgate-api ts",
|
||||
"ts:web": "yarn workspace dbgate-web ts",
|
||||
"ts": "yarn ts:api && yarn ts:web",
|
||||
"postinstall": "patch-package"
|
||||
"postinstall": "patch-package && yarn fillNativeModules"
|
||||
},
|
||||
"dependencies": {
|
||||
"concurrently": "^5.1.0",
|
||||
@@ -43,6 +45,7 @@
|
||||
"socket.io": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.2.0"
|
||||
"copyfiles": "^2.2.0",
|
||||
"prettier": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -5,19 +5,11 @@ SERVER_mysql=localhost
|
||||
USER_mysql=root
|
||||
PASSWORD_mysql=test
|
||||
PORT_mysql=3307
|
||||
ENGINE_mysql=mysql
|
||||
ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
|
||||
LABEL_postgres=Postgres localhost
|
||||
SERVER_postgres=localhost
|
||||
USER_postgres=postgres
|
||||
PASSWORD_postgres=test
|
||||
PORT_postgres=5433
|
||||
ENGINE_postgres=postgres
|
||||
|
||||
TOOLBAR=home
|
||||
ICON_home=mdi mdi-home
|
||||
TITLE_home=Home
|
||||
PAGE_home=home.html
|
||||
STARTUP_PAGES=home
|
||||
|
||||
PAGES_DIRECTORY=/home/jena/jenasoft/dbgate-web/pages
|
||||
ENGINE_postgres=postgres@dbgate-plugin-postgres
|
||||
|
||||
@@ -32,7 +32,7 @@ dbgateApi.runScript(run);
|
||||
|
||||
```
|
||||
|
||||
Silly example, runs without any dependencies. Copy [fakeObjectReader](https://github.com/dbshell/dbgate/blob/master/packages/api/src/shell/fakeObjectReader.js) to [consoleObjectWriter](https://github.com/dbshell/dbgate/blob/master/packages/api/src/shell/consoleObjectWriter.js) .
|
||||
Silly example, runs without any dependencies. Copy [fakeObjectReader](https://github.com/dbgate/dbgate/blob/master/packages/api/src/shell/fakeObjectReader.js) to [consoleObjectWriter](https://github.com/dbgate/dbgate/blob/master/packages/api/src/shell/consoleObjectWriter.js) .
|
||||
|
||||
```javascript
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "dbgate-api",
|
||||
"main": "src/index.js",
|
||||
"version": "1.0.7",
|
||||
"version": "4.0.0",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbshell/dbgate.git"
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur",
|
||||
"author": "Jan Prochazka",
|
||||
"license": "GPL",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"sql",
|
||||
"json",
|
||||
@@ -23,10 +23,11 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"bufferutil": "^4.0.1",
|
||||
"byline": "^5.0.0",
|
||||
"compare-versions": "^3.6.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^6.0.3",
|
||||
"dbgate-sqltree": "^1.0.0",
|
||||
"dbgate-tools": "^1.0.0",
|
||||
"dbgate-sqltree": "^4.0.0",
|
||||
"dbgate-tools": "^4.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"express": "^4.17.1",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
@@ -34,16 +35,21 @@
|
||||
"find-free-port": "^2.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"http": "^0.0.0",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
"line-reader": "^0.4.0",
|
||||
"lodash": "^4.17.15",
|
||||
"ncp": "^2.0.0",
|
||||
"nedb-promises": "^4.0.1",
|
||||
"node-cron": "^2.0.3",
|
||||
"node-ssh-forward": "^0.7.2",
|
||||
"portfinder": "^1.0.28",
|
||||
"simple-encryptor": "^4.0.0",
|
||||
"socket.io": "^2.3.0",
|
||||
"tar": "^6.0.5",
|
||||
"uuid": "^3.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "nodemon src/index.js",
|
||||
"start": "node src/index.js",
|
||||
"start:portal": "env-cmd nodemon src/index.js",
|
||||
"start:covid": "env-cmd -f .covid-env nodemon src/index.js",
|
||||
"ts": "tsc",
|
||||
@@ -51,11 +57,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.149",
|
||||
"dbgate-types": "^1.0.0",
|
||||
"dbgate-types": "^4.0.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"node-loader": "^1.0.2",
|
||||
"nodemon": "^2.0.2",
|
||||
"typescript": "^3.7.4",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"msnodesqlv8": "^2.0.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ module.exports = {
|
||||
type: 'jsonl',
|
||||
},
|
||||
...folders
|
||||
.filter((x) => x != 'default')
|
||||
.map((name) => ({
|
||||
.filter(x => x != 'default')
|
||||
.map(name => ({
|
||||
name,
|
||||
type: 'jsonl',
|
||||
})),
|
||||
@@ -39,8 +39,8 @@ module.exports = {
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = await fs.readdir(dir);
|
||||
return files
|
||||
.filter((name) => name.endsWith('.jsonl'))
|
||||
.map((name) => ({
|
||||
.filter(name => name.endsWith('.jsonl'))
|
||||
.map(name => ({
|
||||
name: name.slice(0, -'.jsonl'.length),
|
||||
type: 'jsonl',
|
||||
}));
|
||||
@@ -84,7 +84,7 @@ module.exports = {
|
||||
});
|
||||
let structure = null;
|
||||
const rows = [];
|
||||
liner.on('line', (line) => {
|
||||
liner.on('line', line => {
|
||||
const data = JSON.parse(line);
|
||||
if (structure) rows.push(data);
|
||||
else structure = data;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const currentVersion = require('../currentVersion');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
|
||||
module.exports = {
|
||||
get_meta: 'get',
|
||||
@@ -31,4 +32,10 @@ module.exports = {
|
||||
...currentVersion,
|
||||
};
|
||||
},
|
||||
|
||||
platformInfo_meta: 'get',
|
||||
async platformInfo() {
|
||||
return platformInfo;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -5,10 +5,12 @@ const nedb = require('nedb-promises');
|
||||
|
||||
const { datadir } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const { encryptConnection } = require('../utility/crypting');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
function getPortalCollections() {
|
||||
if (process.env.CONNECTIONS) {
|
||||
return _.compact(process.env.CONNECTIONS.split(',')).map((id) => ({
|
||||
return _.compact(process.env.CONNECTIONS.split(',')).map(id => ({
|
||||
_id: id,
|
||||
engine: process.env[`ENGINE_${id}`],
|
||||
server: process.env[`SERVER_${id}`],
|
||||
@@ -44,8 +46,9 @@ module.exports = {
|
||||
raw: true,
|
||||
},
|
||||
test(req, res) {
|
||||
const subprocess = fork(process.argv[1], ['connectProcess']);
|
||||
subprocess.on('message', (resp) => {
|
||||
const subprocess = fork(process.argv[1], ['connectProcess', ...process.argv.slice(3)]);
|
||||
subprocess.on('message', resp => {
|
||||
if (handleProcessCommunication(resp, subprocess)) return;
|
||||
// @ts-ignore
|
||||
const { msgtype } = resp;
|
||||
if (msgtype == 'connected' || msgtype == 'error') {
|
||||
@@ -59,10 +62,11 @@ module.exports = {
|
||||
async save(connection) {
|
||||
if (portalConnections) return;
|
||||
let res;
|
||||
const encrypted = encryptConnection(connection);
|
||||
if (connection._id) {
|
||||
res = await this.datastore.update(_.pick(connection, '_id'), connection);
|
||||
res = await this.datastore.update(_.pick(connection, '_id'), encrypted);
|
||||
} else {
|
||||
res = await this.datastore.insert(connection);
|
||||
res = await this.datastore.insert(encrypted);
|
||||
}
|
||||
socket.emitChanged('connection-list-changed');
|
||||
return res;
|
||||
@@ -78,7 +82,7 @@ module.exports = {
|
||||
|
||||
get_meta: 'get',
|
||||
async get({ conid }) {
|
||||
if (portalConnections) return portalConnections.find((x) => x._id == conid);
|
||||
if (portalConnections) return portalConnections.find(x => x._id == conid);
|
||||
const res = await this.datastore.find({ _id: conid });
|
||||
return res[0];
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const { DatabaseAnalyser } = require('dbgate-tools');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
||||
@@ -11,7 +12,7 @@ module.exports = {
|
||||
requests: {},
|
||||
|
||||
handle_structure(conid, database, { structure }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (!existing) return;
|
||||
existing.structure = structure;
|
||||
socket.emitChanged(`database-structure-changed-${conid}-${database}`);
|
||||
@@ -26,7 +27,7 @@ module.exports = {
|
||||
delete this.requests[msgid];
|
||||
},
|
||||
handle_status(conid, database, { status }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (!existing) return;
|
||||
if (existing.status == status) return;
|
||||
existing.status = status;
|
||||
@@ -36,10 +37,10 @@ module.exports = {
|
||||
handle_ping() {},
|
||||
|
||||
async ensureOpened(conid, database) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ conid });
|
||||
const subprocess = fork(process.argv[1], ['databaseConnectionProcess']);
|
||||
const subprocess = fork(process.argv[1], ['databaseConnectionProcess', ...process.argv.slice(3)]);
|
||||
const lastClosed = this.closed[`${conid}/${database}`];
|
||||
const newOpened = {
|
||||
conid,
|
||||
@@ -50,8 +51,10 @@ module.exports = {
|
||||
status: { name: 'pending' },
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
subprocess.on('message', message => {
|
||||
// @ts-ignore
|
||||
const { msgtype } = message;
|
||||
if (handleProcessCommunication(message, subprocess)) return;
|
||||
if (newOpened.disconnected) return;
|
||||
this[`handle_${msgtype}`](conid, database, message);
|
||||
});
|
||||
@@ -82,13 +85,30 @@ module.exports = {
|
||||
async queryData({ conid, database, sql }) {
|
||||
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
// if (opened && opened.status && opened.status.name == 'error') {
|
||||
// return opened.status;
|
||||
// }
|
||||
const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
|
||||
return res;
|
||||
},
|
||||
|
||||
collectionData_meta: 'post',
|
||||
async collectionData({ conid, database, options }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
|
||||
return res.result;
|
||||
},
|
||||
|
||||
updateCollection_meta: 'post',
|
||||
async updateCollection({ conid, database, changeSet }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
|
||||
return res.result;
|
||||
},
|
||||
|
||||
status_meta: 'get',
|
||||
async status({ conid, database }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (existing) return existing.status;
|
||||
const lastClosed = this.closed[`${conid}/${database}`];
|
||||
if (lastClosed) return lastClosed.status;
|
||||
@@ -100,11 +120,18 @@ module.exports = {
|
||||
|
||||
ping_meta: 'post',
|
||||
async ping({ conid, database }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
let existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
|
||||
if (existing) {
|
||||
existing.subprocess.send({ msgtype: 'ping' });
|
||||
} else {
|
||||
existing = await this.ensureOpened(conid, database);
|
||||
}
|
||||
return { status: 'ok' };
|
||||
|
||||
return {
|
||||
status: 'ok',
|
||||
connectionStatus: existing ? existing.status : null,
|
||||
};
|
||||
},
|
||||
|
||||
refresh_meta: 'post',
|
||||
@@ -116,11 +143,11 @@ module.exports = {
|
||||
},
|
||||
|
||||
close(conid, database, kill = true) {
|
||||
const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (existing) {
|
||||
existing.disconnected = true;
|
||||
if (kill) existing.subprocess.kill();
|
||||
this.opened = this.opened.filter((x) => x.conid != conid || x.database != database);
|
||||
this.opened = this.opened.filter(x => x.conid != conid || x.database != database);
|
||||
this.closed[`${conid}/${database}`] = {
|
||||
status: {
|
||||
...existing.status,
|
||||
@@ -144,6 +171,16 @@ module.exports = {
|
||||
// };
|
||||
},
|
||||
|
||||
sqlPreview_meta: 'post',
|
||||
async sqlPreview({ conid, database, objects, options }) {
|
||||
// wait for structure
|
||||
await this.structure({ conid, database });
|
||||
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'sqlPreview', objects, options });
|
||||
return res;
|
||||
},
|
||||
|
||||
// runCommand_meta: 'post',
|
||||
// async runCommand({ conid, database, sql }) {
|
||||
// console.log(`Running SQL command , conid=${conid}, database=${database}, sql=${sql}`);
|
||||
|
||||
@@ -23,7 +23,7 @@ module.exports = {
|
||||
if (!hasPermission(`files/${folder}/read`)) return [];
|
||||
const dir = path.join(filesdir(), folder);
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = (await fs.readdir(dir)).map((file) => ({ folder, file }));
|
||||
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
||||
return files;
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = {
|
||||
for (const folder of folders) {
|
||||
if (!hasPermission(`files/${folder}/read`)) continue;
|
||||
const dir = path.join(filesdir(), folder);
|
||||
const files = (await fs.readdir(dir)).map((file) => ({ folder, file }));
|
||||
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
||||
res.push(...files);
|
||||
}
|
||||
return res;
|
||||
@@ -78,6 +78,11 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
saveAs_meta: 'post',
|
||||
async saveAs({ filePath, data, format }) {
|
||||
await fs.writeFile(filePath, serialize(format, data));
|
||||
},
|
||||
|
||||
favorites_meta: 'get',
|
||||
async favorites() {
|
||||
if (!hasPermission(`files/favorites/read`)) return [];
|
||||
|
||||
@@ -121,7 +121,13 @@ module.exports = {
|
||||
getStats_meta: 'get',
|
||||
getStats({ jslid }) {
|
||||
const file = `${getJslFileName(jslid)}.stats`;
|
||||
if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
||||
if (fs.existsSync(file)) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const fp = require('lodash/fp');
|
||||
const databaseConnections = require('./databaseConnections');
|
||||
|
||||
function pickObjectNames(array) {
|
||||
return _.sortBy(array, (x) => `${x.schemaName}.${x.pureName}`).map(fp.pick(['pureName', 'schemaName']));
|
||||
return _.sortBy(array, x => `${x.schemaName}.${x.pureName}`).map(fp.pick(['pureName', 'schemaName']));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -17,7 +17,7 @@ module.exports = {
|
||||
listObjects_meta: 'get',
|
||||
async listObjects({ conid, database }) {
|
||||
const opened = await databaseConnections.ensureOpened(conid, database);
|
||||
const types = ['tables', 'views', 'procedures', 'functions', 'triggers'];
|
||||
const types = ['tables', 'collections', 'views', 'procedures', 'functions', 'triggers'];
|
||||
return types.reduce(
|
||||
(res, type) => ({
|
||||
...res,
|
||||
@@ -30,18 +30,18 @@ module.exports = {
|
||||
tableInfo_meta: 'get',
|
||||
async tableInfo({ conid, database, schemaName, pureName }) {
|
||||
const opened = await databaseConnections.ensureOpened(conid, database);
|
||||
const table = opened.structure.tables.find((x) => x.pureName == pureName && x.schemaName == schemaName);
|
||||
const allForeignKeys = _.flatten(opened.structure.tables.map((x) => x.foreignKeys));
|
||||
const table = opened.structure.tables.find(x => x.pureName == pureName && x.schemaName == schemaName);
|
||||
const allForeignKeys = _.flatten(opened.structure.tables.map(x => x.foreignKeys));
|
||||
return {
|
||||
...table,
|
||||
dependencies: allForeignKeys.filter((x) => x.refSchemaName == schemaName && x.refTableName == pureName),
|
||||
dependencies: allForeignKeys.filter(x => x.refSchemaName == schemaName && x.refTableName == pureName),
|
||||
};
|
||||
},
|
||||
|
||||
sqlObjectInfo_meta: 'get',
|
||||
async sqlObjectInfo({ objectTypeField, conid, database, schemaName, pureName }) {
|
||||
const opened = await databaseConnections.ensureOpened(conid, database);
|
||||
const res = opened.structure[objectTypeField].find((x) => x.pureName == pureName && x.schemaName == schemaName);
|
||||
const res = opened.structure[objectTypeField].find(x => x.pureName == pureName && x.schemaName == schemaName);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
const fs = require('fs-extra');
|
||||
const axios = require('axios');
|
||||
const path = require('path');
|
||||
const { extractPackageName } = require('dbgate-tools');
|
||||
const { pluginsdir, datadir } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const compareVersions = require('compare-versions');
|
||||
const requirePlugin = require('../shell/requirePlugin');
|
||||
const downloadPackage = require('../utility/downloadPackage');
|
||||
const hasPermission = require('../utility/hasPermission');
|
||||
@@ -25,13 +27,13 @@ const hasPermission = require('../utility/hasPermission');
|
||||
// };
|
||||
// }
|
||||
|
||||
const preinstallPlugins = [
|
||||
'dbgate-plugin-mssql',
|
||||
'dbgate-plugin-mysql',
|
||||
'dbgate-plugin-postgres',
|
||||
'dbgate-plugin-csv',
|
||||
'dbgate-plugin-excel',
|
||||
];
|
||||
const preinstallPluginMinimalVersions = {
|
||||
'dbgate-plugin-mssql': '1.2.1',
|
||||
'dbgate-plugin-mysql': '1.2.1',
|
||||
'dbgate-plugin-postgres': '1.2.1',
|
||||
'dbgate-plugin-csv': '1.0.8',
|
||||
'dbgate-plugin-excel': '1.0.6',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
script_meta: 'get',
|
||||
@@ -50,7 +52,7 @@ module.exports = {
|
||||
`http://registry.npmjs.com/-/v1/search?text=${encodeURIComponent(filter)}+keywords:dbgateplugin&size=25&from=0`
|
||||
);
|
||||
const { objects } = resp.data || {};
|
||||
return (objects || []).map((x) => x.package);
|
||||
return (objects || []).map(x => x.package);
|
||||
},
|
||||
|
||||
info_meta: 'get',
|
||||
@@ -88,9 +90,7 @@ module.exports = {
|
||||
const files = await fs.readdir(pluginsdir());
|
||||
const res = [];
|
||||
for (const packageName of files) {
|
||||
const manifest = await fs
|
||||
.readFile(path.join(pluginsdir(), packageName, 'package.json'))
|
||||
.then((x) => JSON.parse(x));
|
||||
const manifest = await fs.readFile(path.join(pluginsdir(), packageName, 'package.json')).then(x => JSON.parse(x));
|
||||
const readmeFile = path.join(pluginsdir(), packageName, 'README.md');
|
||||
if (await fs.exists(readmeFile)) {
|
||||
manifest.readme = await fs.readFile(readmeFile, { encoding: 'utf-8' });
|
||||
@@ -105,6 +105,10 @@ module.exports = {
|
||||
// );
|
||||
},
|
||||
|
||||
async saveRemovePlugins() {
|
||||
await fs.writeFile(path.join(datadir(), 'removed-plugins'), this.removedPlugins.join('\n'));
|
||||
},
|
||||
|
||||
install_meta: 'post',
|
||||
async install({ packageName }) {
|
||||
if (!hasPermission(`plugins/install`)) return;
|
||||
@@ -113,6 +117,8 @@ module.exports = {
|
||||
await downloadPackage(packageName, dir);
|
||||
}
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
|
||||
await this.saveRemovePlugins();
|
||||
},
|
||||
|
||||
uninstall_meta: 'post',
|
||||
@@ -122,7 +128,19 @@ module.exports = {
|
||||
await fs.rmdir(dir, { recursive: true });
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
this.removedPlugins.push(packageName);
|
||||
await fs.writeFile(path.join(datadir(), 'removed-plugins'), this.removedPlugins.join('\n'));
|
||||
await this.saveRemovePlugins();
|
||||
},
|
||||
|
||||
upgrade_meta: 'post',
|
||||
async upgrade({ packageName }) {
|
||||
if (!hasPermission(`plugins/install`)) return;
|
||||
const dir = path.join(pluginsdir(), packageName);
|
||||
if (await fs.exists(dir)) {
|
||||
await fs.rmdir(dir, { recursive: true });
|
||||
await downloadPackage(packageName, dir);
|
||||
}
|
||||
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
},
|
||||
|
||||
command_meta: 'post',
|
||||
@@ -131,6 +149,14 @@ module.exports = {
|
||||
return content.commands[command](args);
|
||||
},
|
||||
|
||||
authTypes_meta: 'get',
|
||||
async authTypes({ engine }) {
|
||||
const packageName = extractPackageName(engine);
|
||||
const content = requirePlugin(packageName);
|
||||
if (!content.driver || content.driver.engine != engine || !content.driver.getAuthTypes) return null;
|
||||
return content.driver.getAuthTypes() || null;
|
||||
},
|
||||
|
||||
async _init() {
|
||||
const installed = await this.installed();
|
||||
try {
|
||||
@@ -140,9 +166,31 @@ module.exports = {
|
||||
} catch (err) {
|
||||
this.removedPlugins = [];
|
||||
}
|
||||
for (const packageName of preinstallPlugins) {
|
||||
if (this.removedPlugins.includes(packageName)) continue;
|
||||
if (installed.find((x) => x.name == packageName)) continue;
|
||||
|
||||
for (const packageName of Object.keys(preinstallPluginMinimalVersions)) {
|
||||
const installedVersion = installed.find(x => x.name == packageName);
|
||||
if (installedVersion) {
|
||||
// plugin installed, test, whether upgrade
|
||||
const requiredVersion = preinstallPluginMinimalVersions[packageName];
|
||||
if (compareVersions(installedVersion.version, requiredVersion) < 0) {
|
||||
console.log(
|
||||
`Upgrading preinstalled plugin ${packageName}, found ${installedVersion.version}, required version ${requiredVersion}`
|
||||
);
|
||||
await this.upgrade({ packageName });
|
||||
} else {
|
||||
console.log(
|
||||
`Plugin ${packageName} not upgraded, found ${installedVersion.version}, required version ${requiredVersion}`
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.removedPlugins.includes(packageName)) {
|
||||
// plugin was remvoed, don't install again
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Preinstalling plugin', packageName);
|
||||
await this.install({ packageName });
|
||||
|
||||
@@ -7,22 +7,24 @@ const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const { rundir, uploadsdir, pluginsdir } = require('../utility/directories');
|
||||
const { extractShellApiPlugins, extractShellApiFunctionName } = require('dbgate-tools');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
function extractPlugins(script) {
|
||||
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
|
||||
const matches = [...script.matchAll(requireRegex)];
|
||||
return matches.map((x) => x[1]);
|
||||
return matches.map(x => x[1]);
|
||||
}
|
||||
|
||||
const requirePluginsTemplate = (plugins) =>
|
||||
const requirePluginsTemplate = plugins =>
|
||||
plugins
|
||||
.map(
|
||||
(packageName) => `const ${_.camelCase(packageName)} = require(process.env.PLUGIN_${_.camelCase(packageName)});\n`
|
||||
packageName => `const ${_.camelCase(packageName)} = require(process.env.PLUGIN_${_.camelCase(packageName)});\n`
|
||||
)
|
||||
.join('') + `dbgateApi.registerPlugins(${plugins.map((x) => _.camelCase(x)).join(',')});\n`;
|
||||
.join('') + `dbgateApi.registerPlugins(${plugins.map(x => _.camelCase(x)).join(',')});\n`;
|
||||
|
||||
const scriptTemplate = (script) => `
|
||||
const scriptTemplate = script => `
|
||||
const dbgateApi = require(process.env.DBGATE_API);
|
||||
dbgateApi.initializeApiEnvironment();
|
||||
${requirePluginsTemplate(extractPlugins(script))}
|
||||
require=null;
|
||||
async function run() {
|
||||
@@ -35,6 +37,7 @@ dbgateApi.runScript(run);
|
||||
|
||||
const loaderScriptTemplate = (functionName, props, runid) => `
|
||||
const dbgateApi = require(process.env.DBGATE_API);
|
||||
dbgateApi.initializeApiEnvironment();
|
||||
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
|
||||
require=null;
|
||||
async function run() {
|
||||
@@ -92,25 +95,26 @@ module.exports = {
|
||||
const pluginNames = fs.readdirSync(pluginsdir());
|
||||
console.log(`RUNNING SCRIPT ${scriptFile}`);
|
||||
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
|
||||
const subprocess = fork(scriptFile, ['--checkParent'], {
|
||||
const subprocess = fork(scriptFile, ['--checkParent', ...process.argv.slice(3)], {
|
||||
cwd: directory,
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||
env: {
|
||||
DBGATE_API: process.argv[1],
|
||||
..._.fromPairs(pluginNames.map((name) => [`PLUGIN_${_.camelCase(name)}`, path.join(pluginsdir(), name)])),
|
||||
...process.env,
|
||||
DBGATE_API: global['dbgateApiModulePath'] || process.argv[1],
|
||||
..._.fromPairs(pluginNames.map(name => [`PLUGIN_${_.camelCase(name)}`, path.join(pluginsdir(), name)])),
|
||||
},
|
||||
});
|
||||
const pipeDispatcher = (severity) => (data) =>
|
||||
const pipeDispatcher = severity => data =>
|
||||
this.dispatchMessage(runid, { severity, message: data.toString().trim() });
|
||||
|
||||
byline(subprocess.stdout).on('data', pipeDispatcher('info'));
|
||||
byline(subprocess.stderr).on('data', pipeDispatcher('error'));
|
||||
subprocess.on('exit', (code) => {
|
||||
subprocess.on('exit', code => {
|
||||
this.rejectRequest(runid, { message: 'No data retured, maybe input data source is too big' });
|
||||
console.log('... EXIT process', code);
|
||||
socket.emit(`runner-done-${runid}`, code);
|
||||
});
|
||||
subprocess.on('error', (error) => {
|
||||
subprocess.on('error', error => {
|
||||
this.rejectRequest(runid, { message: error && (error.message || error.toString()) });
|
||||
console.error('... ERROR subprocess', error);
|
||||
this.dispatchMessage({
|
||||
@@ -123,8 +127,10 @@ module.exports = {
|
||||
subprocess,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
subprocess.on('message', message => {
|
||||
// @ts-ignore
|
||||
const { msgtype } = message;
|
||||
if (handleProcessCommunication(message, subprocess)) return;
|
||||
this[`handle_${msgtype}`](runid, message);
|
||||
});
|
||||
return newOpened;
|
||||
@@ -138,7 +144,7 @@ module.exports = {
|
||||
|
||||
cancel_meta: 'post',
|
||||
async cancel({ runid }) {
|
||||
const runner = this.opened.find((x) => x.runid == runid);
|
||||
const runner = this.opened.find(x => x.runid == runid);
|
||||
if (!runner) {
|
||||
throw new Error('Invalid runner');
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
tasks: [],
|
||||
|
||||
async unload() {
|
||||
this.tasks.forEach((x) => x.destroy());
|
||||
this.tasks.forEach(x => x.destroy());
|
||||
this.tasks = [];
|
||||
},
|
||||
|
||||
|
||||
@@ -2,19 +2,23 @@ const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const _ = require('lodash');
|
||||
const AsyncLock = require('async-lock');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const lock = new AsyncLock();
|
||||
|
||||
module.exports = {
|
||||
opened: [],
|
||||
closed: {},
|
||||
lastPinged: {},
|
||||
|
||||
handle_databases(conid, { databases }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid);
|
||||
const existing = this.opened.find(x => x.conid == conid);
|
||||
if (!existing) return;
|
||||
existing.databases = databases;
|
||||
socket.emitChanged(`database-list-changed-${conid}`);
|
||||
},
|
||||
handle_status(conid, { status }) {
|
||||
const existing = this.opened.find((x) => x.conid == conid);
|
||||
const existing = this.opened.find(x => x.conid == conid);
|
||||
if (!existing) return;
|
||||
existing.status = status;
|
||||
socket.emitChanged(`server-status-changed`);
|
||||
@@ -22,42 +26,47 @@ module.exports = {
|
||||
handle_ping() {},
|
||||
|
||||
async ensureOpened(conid) {
|
||||
const existing = this.opened.find((x) => x.conid == conid);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ conid });
|
||||
const subprocess = fork(process.argv[1], ['serverConnectionProcess']);
|
||||
const newOpened = {
|
||||
conid,
|
||||
subprocess,
|
||||
databases: [],
|
||||
connection,
|
||||
status: {
|
||||
name: 'pending',
|
||||
},
|
||||
disconnected: false,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
delete this.closed[conid];
|
||||
socket.emitChanged(`server-status-changed`);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
if (newOpened.disconnected) return;
|
||||
this[`handle_${msgtype}`](conid, message);
|
||||
const res = await lock.acquire(conid, async () => {
|
||||
const existing = this.opened.find(x => x.conid == conid);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ conid });
|
||||
const subprocess = fork(process.argv[1], ['serverConnectionProcess', ...process.argv.slice(3)]);
|
||||
const newOpened = {
|
||||
conid,
|
||||
subprocess,
|
||||
databases: [],
|
||||
connection,
|
||||
status: {
|
||||
name: 'pending',
|
||||
},
|
||||
disconnected: false,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
delete this.closed[conid];
|
||||
socket.emitChanged(`server-status-changed`);
|
||||
subprocess.on('message', message => {
|
||||
// @ts-ignore
|
||||
const { msgtype } = message;
|
||||
if (handleProcessCommunication(message, subprocess)) return;
|
||||
if (newOpened.disconnected) return;
|
||||
this[`handle_${msgtype}`](conid, message);
|
||||
});
|
||||
subprocess.on('exit', () => {
|
||||
if (newOpened.disconnected) return;
|
||||
this.close(conid, false);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection });
|
||||
return newOpened;
|
||||
});
|
||||
subprocess.on('exit', () => {
|
||||
if (newOpened.disconnected) return;
|
||||
this.close(conid, false);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection });
|
||||
return newOpened;
|
||||
return res;
|
||||
},
|
||||
|
||||
close(conid, kill = true) {
|
||||
const existing = this.opened.find((x) => x.conid == conid);
|
||||
const existing = this.opened.find(x => x.conid == conid);
|
||||
if (existing) {
|
||||
existing.disconnected = true;
|
||||
if (kill) existing.subprocess.kill();
|
||||
this.opened = this.opened.filter((x) => x.conid != conid);
|
||||
this.opened = this.opened.filter(x => x.conid != conid);
|
||||
this.closed[conid] = {
|
||||
...existing.status,
|
||||
name: 'error',
|
||||
@@ -82,10 +91,17 @@ module.exports = {
|
||||
|
||||
ping_meta: 'post',
|
||||
async ping({ connections }) {
|
||||
for (const conid of connections) {
|
||||
const opened = await this.ensureOpened(conid);
|
||||
opened.subprocess.send({ msgtype: 'ping' });
|
||||
}
|
||||
await Promise.all(
|
||||
_.uniq(connections).map(async conid => {
|
||||
const last = this.lastPinged[conid];
|
||||
if (last && new Date().getTime() - last < 30 * 1000) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.lastPinged[conid] = new Date().getTime();
|
||||
const opened = await this.ensureOpened(conid);
|
||||
opened.subprocess.send({ msgtype: 'ping' });
|
||||
})
|
||||
);
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const jsldata = require('./jsldata');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('dbgate-types').OpenedSession[]} */
|
||||
@@ -64,7 +65,7 @@ module.exports = {
|
||||
async create({ conid, database }) {
|
||||
const sesid = uuidv1();
|
||||
const connection = await connections.get({ conid });
|
||||
const subprocess = fork(process.argv[1], ['sessionProcess']);
|
||||
const subprocess = fork(process.argv[1], ['sessionProcess', ...process.argv.slice(3)]);
|
||||
const newOpened = {
|
||||
conid,
|
||||
database,
|
||||
@@ -73,8 +74,10 @@ module.exports = {
|
||||
sesid,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
subprocess.on('message', message => {
|
||||
// @ts-ignore
|
||||
const { msgtype } = message;
|
||||
if (handleProcessCommunication(message, subprocess)) return;
|
||||
this[`handle_${msgtype}`](sesid, message);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection, database });
|
||||
@@ -83,7 +86,7 @@ module.exports = {
|
||||
|
||||
executeQuery_meta: 'post',
|
||||
async executeQuery({ sesid, sql }) {
|
||||
const session = this.opened.find((x) => x.sesid == sesid);
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid session');
|
||||
}
|
||||
@@ -107,7 +110,7 @@ module.exports = {
|
||||
|
||||
kill_meta: 'post',
|
||||
async kill({ sesid }) {
|
||||
const session = this.opened.find((x) => x.sesid == sesid);
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid session');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
module.exports = {
|
||||
version: '3.8.6',
|
||||
buildTime: '2020-12-10T11:14:01.053Z'
|
||||
version: '4.0.0',
|
||||
buildTime: '2021-04-01T10:48:22.253Z'
|
||||
};
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
const shell = require('./shell');
|
||||
// require('socket.io-client');
|
||||
|
||||
// "socket.io-client": "^2.3.0",
|
||||
// "utf-8-validate": "^5.0.2",
|
||||
// "uuid": "^3.4.0",
|
||||
// "uws": "10.148.1"
|
||||
|
||||
const argument = process.argv[2];
|
||||
if (argument && argument.endsWith('Process')) {
|
||||
@@ -18,4 +12,7 @@ if (argument && argument.endsWith('Process')) {
|
||||
main.start(argument);
|
||||
}
|
||||
|
||||
module.exports = shell;
|
||||
module.exports = {
|
||||
...shell,
|
||||
getMainModule: () => require('./main'),
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ const io = require('socket.io');
|
||||
const fs = require('fs');
|
||||
const findFreePort = require('find-free-port');
|
||||
const childProcessChecker = require('./utility/childProcessChecker');
|
||||
const path = require('path');
|
||||
|
||||
const useController = require('./utility/useController');
|
||||
const socket = require('./utility/socket');
|
||||
@@ -78,13 +79,15 @@ function start(argument = null) {
|
||||
|
||||
app.use('/runners/data', express.static(rundir()));
|
||||
|
||||
if (fs.existsSync('/home/dbgate-docker/build')) {
|
||||
if (fs.existsSync('/home/dbgate-docker/public')) {
|
||||
// server static files inside docker container
|
||||
app.use(express.static('/home/dbgate-docker/build'));
|
||||
app.use(express.static('/home/dbgate-docker/public'));
|
||||
} else {
|
||||
app.get('/', (req, res) => {
|
||||
res.send('DbGate API');
|
||||
});
|
||||
if (argument != 'startNodeWeb') {
|
||||
app.get('/', (req, res) => {
|
||||
res.send('DbGate API');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (argument == '--dynport') {
|
||||
@@ -96,6 +99,13 @@ function start(argument = null) {
|
||||
process.send({ msgtype: 'listening', port });
|
||||
});
|
||||
});
|
||||
} else if (argument == 'startNodeWeb') {
|
||||
app.use(express.static(path.join(__dirname, '../../dbgate-web/public')));
|
||||
findFreePort(5000, function (err, port) {
|
||||
server.listen(port, () => {
|
||||
console.log(`DbGate API listening on port ${port}`);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
server.listen(3000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
const argIndex = process.argv.indexOf('--native-modules');
|
||||
const redirectFile = argIndex > 0 ? process.argv[argIndex + 1] : null;
|
||||
|
||||
// @ts-ignore
|
||||
module.exports = redirectFile ? __non_webpack_require__(redirectFile) : require('./nativeModulesContent');
|
||||
@@ -1,12 +1,15 @@
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
process.on('message', async (connection) => {
|
||||
process.on('message', async connection => {
|
||||
if (handleProcessCommunication(connection)) return;
|
||||
try {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const conn = await driver.connect(connection);
|
||||
const conn = await connectUtility(driver, connection);
|
||||
const res = await driver.getVersion(conn);
|
||||
process.send({ msgtype: 'connected', ...res });
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const { SqlGenerator } = require('dbgate-tools');
|
||||
|
||||
let systemConnection;
|
||||
let storedConnection;
|
||||
@@ -59,7 +62,7 @@ async function handleConnect({ connection, structure }) {
|
||||
|
||||
if (!structure) setStatusName('pending');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await checkedAsyncCall(driver.connect(storedConnection));
|
||||
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection));
|
||||
if (structure) {
|
||||
analysedStructure = structure;
|
||||
handleIncrementalRefresh();
|
||||
@@ -91,6 +94,49 @@ async function handleQueryData({ msgid, sql }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCollectionData({ msgid, options }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
const result = await driver.readCollection(systemConnection, options);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUpdateCollection({ msgid, changeSet }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
const result = await driver.updateCollection(systemConnection, changeSet);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSqlPreview({ msgid, objects, options }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
|
||||
try {
|
||||
const dmp = driver.createDumper();
|
||||
const generator = new SqlGenerator(analysedStructure, options, objects, dmp, driver, systemConnection);
|
||||
|
||||
await generator.dump();
|
||||
process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
|
||||
if (generator.isUnhandledException) {
|
||||
setTimeout(() => {
|
||||
console.log('Exiting because of unhandled exception');
|
||||
process.exit(0);
|
||||
}, 500);
|
||||
}
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, isError: true, errorMessage: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
// async function handleRunCommand({ msgid, sql }) {
|
||||
// await waitConnected();
|
||||
// const driver = engines(storedConnection);
|
||||
@@ -105,6 +151,9 @@ function handlePing() {
|
||||
const messageHandlers = {
|
||||
connect: handleConnect,
|
||||
queryData: handleQueryData,
|
||||
updateCollection: handleUpdateCollection,
|
||||
collectionData: handleCollectionData,
|
||||
sqlPreview: handleSqlPreview,
|
||||
ping: handlePing,
|
||||
// runCommand: handleRunCommand,
|
||||
};
|
||||
@@ -119,12 +168,14 @@ function start() {
|
||||
|
||||
setInterval(() => {
|
||||
const time = new Date().getTime();
|
||||
if (time - lastPing > 60 * 1000) {
|
||||
if (time - lastPing > 120 * 1000) {
|
||||
console.log('Database connection not alive, exiting');
|
||||
process.exit(0);
|
||||
}
|
||||
}, 60 * 1000);
|
||||
|
||||
process.on('message', async (message) => {
|
||||
process.on('message', async message => {
|
||||
if (handleProcessCommunication(message)) return;
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const JsonLinesDatastore = require('../utility/JsonLinesDatastore');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
let lastPing = null;
|
||||
let datastore = new JsonLinesDatastore();
|
||||
@@ -46,7 +47,8 @@ function start() {
|
||||
}
|
||||
}, 60 * 1000);
|
||||
|
||||
process.on('message', async (message) => {
|
||||
process.on('message', async message => {
|
||||
if (handleProcessCommunication(message)) return;
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
let systemConnection;
|
||||
let storedConnection;
|
||||
@@ -47,7 +50,7 @@ async function handleConnect(connection) {
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
systemConnection = await driver.connect(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
handleRefresh();
|
||||
setInterval(handleRefresh, 30 * 1000);
|
||||
} catch (err) {
|
||||
@@ -66,9 +69,13 @@ function handlePing() {
|
||||
|
||||
async function handleCreateDatabase({ name }) {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await driver.connect(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
|
||||
await driver.query(systemConnection, `CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
|
||||
if (driver.createDatabase) {
|
||||
await driver.createDatabase(systemConnection, name);
|
||||
} else {
|
||||
await driver.query(systemConnection, `CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
|
||||
}
|
||||
await handleRefresh();
|
||||
}
|
||||
|
||||
@@ -88,12 +95,14 @@ function start() {
|
||||
|
||||
setInterval(() => {
|
||||
const time = new Date().getTime();
|
||||
if (time - lastPing > 60 * 1000) {
|
||||
if (time - lastPing > 120 * 1000) {
|
||||
console.log('Server connection not alive, exiting');
|
||||
process.exit(0);
|
||||
}
|
||||
}, 60 * 1000);
|
||||
|
||||
process.on('message', async (message) => {
|
||||
process.on('message', async message => {
|
||||
if (handleProcessCommunication(message)) return;
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (err) {
|
||||
|
||||
@@ -7,6 +7,9 @@ const goSplit = require('../utility/goSplit');
|
||||
|
||||
const { jsldir } = require('../utility/directories');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
let systemConnection;
|
||||
let storedConnection;
|
||||
@@ -14,12 +17,18 @@ let afterConnectCallbacks = [];
|
||||
// let currentHandlers = [];
|
||||
|
||||
class TableWriter {
|
||||
constructor(columns, resultIndex) {
|
||||
constructor(structure, resultIndex) {
|
||||
this.jslid = uuidv1();
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
this.currentRowCount = 0;
|
||||
this.currentChangeIndex = 1;
|
||||
fs.writeFileSync(this.currentFile, JSON.stringify({ columns }) + '\n');
|
||||
fs.writeFileSync(
|
||||
this.currentFile,
|
||||
JSON.stringify({
|
||||
...structure,
|
||||
__isStreamHeader: true,
|
||||
}) + '\n'
|
||||
);
|
||||
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
||||
this.writeCurrentStats(false, false);
|
||||
this.resultIndex = resultIndex;
|
||||
@@ -73,7 +82,7 @@ class StreamHandler {
|
||||
|
||||
// use this for cancelling - not implemented
|
||||
// this.stream = null;
|
||||
|
||||
|
||||
this.plannedStats = false;
|
||||
this.resultIndexHolder = resultIndexHolder;
|
||||
this.resolve = resolve;
|
||||
@@ -89,7 +98,7 @@ class StreamHandler {
|
||||
|
||||
recordset(columns) {
|
||||
this.closeCurrentWriter();
|
||||
this.currentWriter = new TableWriter(columns, this.resultIndexHolder.value);
|
||||
this.currentWriter = new TableWriter(Array.isArray(columns) ? { columns } : columns, this.resultIndexHolder.value);
|
||||
this.resultIndexHolder.value += 1;
|
||||
|
||||
// this.writeCurrentStats();
|
||||
@@ -130,7 +139,7 @@ async function handleConnect(connection) {
|
||||
storedConnection = connection;
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await driver.connect(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
for (const [resolve] of afterConnectCallbacks) {
|
||||
resolve();
|
||||
}
|
||||
@@ -180,7 +189,8 @@ async function handleMessage({ msgtype, ...other }) {
|
||||
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
process.on('message', async (message) => {
|
||||
process.on('message', async message => {
|
||||
if (handleProcessCommunication(message)) return;
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
|
||||
|
||||
function copyStream(input, output) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ensureHeader = new EnsureStreamHeaderStream();
|
||||
const finisher = output['finisher'] || output;
|
||||
finisher.on('finish', resolve);
|
||||
finisher.on('error', reject);
|
||||
input.pipe(output);
|
||||
input.pipe(ensureHeader);
|
||||
ensureHeader.pipe(output);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const goSplit = require('../utility/goSplit');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function executeQuery({ connection, sql }) {
|
||||
console.log(`Execute query ${sql}`);
|
||||
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await driver.connect(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
console.log(`Connected.`);
|
||||
|
||||
for (const sqlItem of goSplit(sql)) {
|
||||
|
||||
@@ -17,6 +17,7 @@ const requirePlugin = require('./requirePlugin');
|
||||
const download = require('./download');
|
||||
const executeQuery = require('./executeQuery');
|
||||
const loadFile = require('./loadFile');
|
||||
const initializeApiEnvironment = require('./initializeApiEnvironment');
|
||||
|
||||
const dbgateApi = {
|
||||
queryReader,
|
||||
@@ -37,8 +38,9 @@ const dbgateApi = {
|
||||
registerPlugins,
|
||||
executeQuery,
|
||||
loadFile,
|
||||
initializeApiEnvironment,
|
||||
};
|
||||
|
||||
requirePlugin.initialize(dbgateApi);
|
||||
requirePlugin.initializeDbgateApi(dbgateApi);
|
||||
|
||||
module.exports = dbgateApi;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
async function initializeApiEnvironment() {
|
||||
process.on('message', async message => {
|
||||
handleProcessCommunication(message);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = initializeApiEnvironment;
|
||||
@@ -3,9 +3,8 @@ const stream = require('stream');
|
||||
const byline = require('byline');
|
||||
|
||||
class ParseStream extends stream.Transform {
|
||||
constructor({ header, limitRows }) {
|
||||
constructor({ limitRows }) {
|
||||
super({ objectMode: true });
|
||||
this.header = header;
|
||||
this.wasHeader = false;
|
||||
this.limitRows = limitRows;
|
||||
this.rowsWritten = 0;
|
||||
@@ -13,7 +12,14 @@ class ParseStream extends stream.Transform {
|
||||
_transform(chunk, encoding, done) {
|
||||
const obj = JSON.parse(chunk);
|
||||
if (!this.wasHeader) {
|
||||
if (!this.header) this.push({ columns: Object.keys(obj).map((columnName) => ({ columnName })) });
|
||||
if (
|
||||
!obj.__isStreamHeader &&
|
||||
// TODO remove isArray test
|
||||
!Array.isArray(obj.columns)
|
||||
) {
|
||||
this.push({ columns: Object.keys(obj).map(columnName => ({ columnName })) });
|
||||
}
|
||||
|
||||
this.wasHeader = true;
|
||||
}
|
||||
if (!this.limitRows || this.rowsWritten < this.limitRows) {
|
||||
@@ -24,12 +30,12 @@ class ParseStream extends stream.Transform {
|
||||
}
|
||||
}
|
||||
|
||||
async function jsonLinesReader({ fileName, encoding = 'utf-8', header = true, limitRows = undefined }) {
|
||||
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
|
||||
console.log(`Reading file ${fileName}`);
|
||||
|
||||
const fileStream = fs.createReadStream(fileName, encoding);
|
||||
const liner = byline(fileStream);
|
||||
const parser = new ParseStream({ header, limitRows });
|
||||
const parser = new ParseStream({ limitRows });
|
||||
liner.pipe(parser);
|
||||
return parser;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,16 @@ class StringifyStream extends stream.Transform {
|
||||
this.wasHeader = false;
|
||||
}
|
||||
_transform(chunk, encoding, done) {
|
||||
let skip = false;
|
||||
if (!this.wasHeader) {
|
||||
if (this.header) this.push(JSON.stringify(chunk) + '\n');
|
||||
skip =
|
||||
(chunk.__isStreamHeader ||
|
||||
// TODO remove isArray test
|
||||
Array.isArray(chunk.columns)) &&
|
||||
!this.header;
|
||||
this.wasHeader = true;
|
||||
} else {
|
||||
}
|
||||
if (!skip) {
|
||||
this.push(JSON.stringify(chunk) + '\n');
|
||||
}
|
||||
done();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
const requireEngineDriver = require("../utility/requireEngineDriver");
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function queryReader({ connection, sql }) {
|
||||
console.log(`Reading query ${sql}`);
|
||||
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await driver.connect(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
console.log(`Connected.`);
|
||||
return await driver.readQuery(pool, sql);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
const path = require('path');
|
||||
const { pluginsdir } = require('../utility/directories');
|
||||
const nativeModules = require('../nativeModules');
|
||||
|
||||
const loadedPlugins = {};
|
||||
|
||||
const dbgateEnv = {
|
||||
dbgateApi: null,
|
||||
nativeModules,
|
||||
};
|
||||
|
||||
function requirePlugin(packageName, requiredPlugin = null) {
|
||||
@@ -19,7 +21,7 @@ function requirePlugin(packageName, requiredPlugin = null) {
|
||||
// @ts-ignore
|
||||
module = __non_webpack_require__(modulePath);
|
||||
} catch (err) {
|
||||
console.error('Failed load webpacked module', err);
|
||||
console.log('Failed load webpacked module', err.message);
|
||||
module = require(modulePath);
|
||||
}
|
||||
requiredPlugin = module.__esModule ? module.default : module;
|
||||
@@ -30,7 +32,7 @@ function requirePlugin(packageName, requiredPlugin = null) {
|
||||
return requiredPlugin;
|
||||
}
|
||||
|
||||
requirePlugin.initialize = (value) => {
|
||||
requirePlugin.initializeDbgateApi = value => {
|
||||
dbgateEnv.dbgateApi = value;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
const { quoteFullName, fullNameToString } = require('dbgate-tools');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function tableReader({ connection, pureName, schemaName }) {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await driver.connect(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
console.log(`Connected.`);
|
||||
|
||||
const fullName = { pureName, schemaName };
|
||||
|
||||
if (driver.dialect.nosql) {
|
||||
// @ts-ignore
|
||||
console.log(`Reading collection ${fullNameToString(fullName)}`);
|
||||
// @ts-ignore
|
||||
return await driver.readQuery(pool, JSON.stringify(fullName));
|
||||
}
|
||||
|
||||
const table = await driver.analyseSingleObject(pool, fullName, 'tables');
|
||||
const query = `select * from ${quoteFullName(driver.dialect, fullName)}`;
|
||||
if (table) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const { fullNameToString } = require('dbgate-tools');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function tableWriter({ connection, schemaName, pureName, ...options }) {
|
||||
console.log(`Writing table ${fullNameToString({ schemaName, pureName })}`);
|
||||
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await driver.connect(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
console.log(`Connected.`);
|
||||
return await driver.writeTable(pool, { schemaName, pureName }, options);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { fork } = require('child_process');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { handleProcessCommunication } = require('./processComm');
|
||||
|
||||
class DatastoreProxy {
|
||||
constructor(file) {
|
||||
@@ -28,10 +29,13 @@ class DatastoreProxy {
|
||||
|
||||
async ensureSubprocess() {
|
||||
if (!this.subprocess) {
|
||||
this.subprocess = fork(process.argv[1], ['jslDatastoreProcess']);
|
||||
this.subprocess = fork(process.argv[1], ['jslDatastoreProcess', ...process.argv.slice(3)]);
|
||||
|
||||
this.subprocess.on('message', message => {
|
||||
// @ts-ignore
|
||||
const { msgtype } = message;
|
||||
if (handleProcessCommunication(message, this.subprocess)) return;
|
||||
|
||||
// @ts-ignore
|
||||
this.subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
// if (this.disconnected) return;
|
||||
this[`handle_${msgtype}`](message);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
const stream = require('stream');
|
||||
|
||||
class EnsureStreamHeaderStream extends stream.Transform {
|
||||
constructor() {
|
||||
super({ objectMode: true });
|
||||
this.wasHeader = false;
|
||||
}
|
||||
_transform(chunk, encoding, done) {
|
||||
if (!this.wasHeader) {
|
||||
if (chunk.__isDynamicStructure) {
|
||||
// ignore dynamic structure header
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!chunk.__isStreamHeader &&
|
||||
// TODO remove isArray test
|
||||
!Array.isArray(chunk.columns)
|
||||
) {
|
||||
this.push({
|
||||
__isStreamHeader: true,
|
||||
__isComputedStructure: true,
|
||||
columns: Object.keys(chunk).map(columnName => ({ columnName })),
|
||||
});
|
||||
}
|
||||
|
||||
this.wasHeader = true;
|
||||
}
|
||||
this.push(chunk);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EnsureStreamHeaderStream;
|
||||
@@ -1,5 +1,5 @@
|
||||
let counter = 0;
|
||||
|
||||
|
||||
function childProcessChecker() {
|
||||
setInterval(() => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
const { SSHConnection } = require('node-ssh-forward');
|
||||
const portfinder = require('portfinder');
|
||||
const fs = require('fs-extra');
|
||||
const { decryptConnection } = require('./crypting');
|
||||
const { getSshTunnel } = require('./sshTunnel');
|
||||
const { getSshTunnelProxy } = require('./sshTunnelProxy');
|
||||
|
||||
async function connectUtility(driver, storedConnection) {
|
||||
const connection = {
|
||||
...decryptConnection(storedConnection),
|
||||
};
|
||||
|
||||
if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
|
||||
|
||||
if (connection.useSshTunnel) {
|
||||
const tunnel = await getSshTunnelProxy(connection);
|
||||
if (tunnel.state == 'error') {
|
||||
throw new Error(tunnel.message);
|
||||
}
|
||||
|
||||
connection.server = '127.0.0.1';
|
||||
connection.port = tunnel.localPort;
|
||||
}
|
||||
|
||||
// SSL functionality - copied from https://github.com/beekeeper-studio/beekeeper-studio
|
||||
if (connection.useSsl) {
|
||||
connection.ssl = {};
|
||||
|
||||
if (connection.sslCaFile) {
|
||||
connection.ssl.ca = await fs.readFile(connection.sslCaFile);
|
||||
}
|
||||
|
||||
if (connection.sslCertFile) {
|
||||
connection.ssl.cert = await fs.readFile(connection.sslCertFile);
|
||||
}
|
||||
|
||||
if (connection.sslKeyFile) {
|
||||
connection.ssl.key = await fs.readFile(connection.sslKeyFile);
|
||||
}
|
||||
|
||||
if (!connection.ssl.key && !connection.ssl.ca && !connection.ssl.cert) {
|
||||
// TODO: provide this as an option in settings
|
||||
// or per-connection as 'reject self-signed certs'
|
||||
// How it works:
|
||||
// if false, cert can be self-signed
|
||||
// if true, has to be from a public CA
|
||||
// Heroku certs are self-signed.
|
||||
// if you provide ca/cert/key files, it overrides this
|
||||
connection.ssl.rejectUnauthorized = false;
|
||||
} else {
|
||||
connection.ssl.rejectUnauthorized = connection.sslRejectUnauthorized;
|
||||
}
|
||||
}
|
||||
|
||||
const conn = await driver.connect(connection);
|
||||
return conn;
|
||||
}
|
||||
|
||||
module.exports = connectUtility;
|
||||
@@ -0,0 +1,88 @@
|
||||
const crypto = require('crypto');
|
||||
const simpleEncryptor = require('simple-encryptor');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { datadir } = require('./directories');
|
||||
|
||||
const defaultEncryptionKey = 'mQAUaXhavRGJDxDTXSCg7Ej0xMmGCrx6OKA07DIMBiDcYYkvkaXjTAzPUEHEHEf9';
|
||||
|
||||
let _encryptionKey = null;
|
||||
|
||||
function loadEncryptionKey() {
|
||||
if (_encryptionKey) {
|
||||
return _encryptionKey;
|
||||
}
|
||||
const encryptor = simpleEncryptor.createEncryptor(defaultEncryptionKey);
|
||||
|
||||
const keyFile = path.join(datadir(), '.key');
|
||||
|
||||
if (!fs.existsSync(keyFile)) {
|
||||
const generatedKey = crypto.randomBytes(32);
|
||||
const newKey = generatedKey.toString('hex');
|
||||
const result = {
|
||||
encryptionKey: newKey,
|
||||
};
|
||||
fs.writeFileSync(keyFile, encryptor.encrypt(result), 'utf-8');
|
||||
}
|
||||
|
||||
const encryptedData = fs.readFileSync(keyFile, 'utf-8');
|
||||
const data = encryptor.decrypt(encryptedData);
|
||||
_encryptionKey = data['encryptionKey'];
|
||||
return _encryptionKey;
|
||||
}
|
||||
|
||||
let _encryptor = null;
|
||||
|
||||
function getEncryptor() {
|
||||
if (_encryptor) {
|
||||
return _encryptor;
|
||||
}
|
||||
_encryptor = simpleEncryptor.createEncryptor(loadEncryptionKey());
|
||||
return _encryptor;
|
||||
}
|
||||
|
||||
function encryptPasswordField(connection, field) {
|
||||
if (
|
||||
connection &&
|
||||
connection[field] &&
|
||||
!connection[field].startsWith('crypt:') &&
|
||||
connection.passwordMode != 'saveRaw'
|
||||
) {
|
||||
return {
|
||||
...connection,
|
||||
[field]: 'crypt:' + getEncryptor().encrypt(connection[field]),
|
||||
};
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
function decryptPasswordField(connection, field) {
|
||||
if (connection && connection[field] && connection[field].startsWith('crypt:')) {
|
||||
return {
|
||||
...connection,
|
||||
[field]: getEncryptor().decrypt(connection[field].substring('crypt:'.length)),
|
||||
};
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
function encryptConnection(connection) {
|
||||
connection = encryptPasswordField(connection, 'password');
|
||||
connection = encryptPasswordField(connection, 'sshPassword');
|
||||
connection = encryptPasswordField(connection, 'sshKeyFilePassword');
|
||||
return connection;
|
||||
}
|
||||
|
||||
function decryptConnection(connection) {
|
||||
connection = decryptPasswordField(connection, 'password');
|
||||
connection = decryptPasswordField(connection, 'sshPassword');
|
||||
connection = decryptPasswordField(connection, 'sshKeyFilePassword');
|
||||
return connection;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadEncryptionKey,
|
||||
encryptConnection,
|
||||
decryptConnection,
|
||||
};
|
||||
@@ -15,15 +15,14 @@ function extractTarball(tmpFile, destination) {
|
||||
fs.createReadStream(tmpFile)
|
||||
.pipe(zlib.createGunzip())
|
||||
.pipe(tar.extract({ cwd: destination }))
|
||||
.on('error', (err) => reject(err))
|
||||
.on('error', err => reject(err))
|
||||
.on('end', () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function copyDirectory(source, target) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ncp(source, target, (err) => {
|
||||
ncp(source, target, err => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const p = process;
|
||||
const platform = p.env.OS_OVERRIDE ? p.env.OS_OVERRIDE : p.platform;
|
||||
const isWindows = platform === 'win32';
|
||||
const isMac = platform === 'darwin';
|
||||
const isLinux = platform === 'linux';
|
||||
const isDocker = fs.existsSync('/home/dbgate-docker/build');
|
||||
|
||||
const platformInfo = {
|
||||
isWindows,
|
||||
isMac,
|
||||
isLinux,
|
||||
isDocker,
|
||||
isSnap: p.env.ELECTRON_SNAP == 'true',
|
||||
isPortable: isWindows && p.env.PORTABLE_EXECUTABLE_DIR,
|
||||
isAppImage: p.env.DESKTOPINTEGRATION === 'AppImageLauncher',
|
||||
sshAuthSock: p.env.SSH_AUTH_SOCK,
|
||||
environment: process.env.NODE_ENV,
|
||||
platform,
|
||||
runningInWebpack: !!p.env.WEBPACK_DEV_SERVER_URL,
|
||||
defaultKeyFile: path.join(os.homedir(), '.ssh/id_rsa'),
|
||||
};
|
||||
|
||||
module.exports = platformInfo;
|
||||
@@ -0,0 +1,18 @@
|
||||
const { handleGetSshTunnelRequest, handleGetSshTunnelResponse } = require('./sshTunnelProxy');
|
||||
|
||||
function handleProcessCommunication(message, subprocess) {
|
||||
const { msgtype } = message;
|
||||
if (msgtype == 'getsshtunnel-request') {
|
||||
handleGetSshTunnelRequest(message, subprocess);
|
||||
return true;
|
||||
}
|
||||
if (msgtype == 'getsshtunnel-response') {
|
||||
handleGetSshTunnelResponse(message, subprocess);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleProcessCommunication,
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
const _ = require('lodash');
|
||||
const requirePlugin = require('../shell/requirePlugin');
|
||||
|
||||
|
||||
/** @returns {import('dbgate-types').EngineDriver} */
|
||||
function requireEngineDriver(connection) {
|
||||
let engine = null;
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
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 sshConnectionCache = {};
|
||||
const sshTunnelCache = {};
|
||||
|
||||
const CONNECTION_FIELDS = [
|
||||
'sshHost',
|
||||
'sshPort',
|
||||
'sshLogin',
|
||||
'sshPassword',
|
||||
'sshMode',
|
||||
'sshKeyFile',
|
||||
'sshBastionHost',
|
||||
'sshKeyFilePassword',
|
||||
];
|
||||
const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
|
||||
|
||||
async function getSshConnection(connection) {
|
||||
const connectionCacheKey = stableStringify(_.pick(connection, CONNECTION_FIELDS));
|
||||
if (sshConnectionCache[connectionCacheKey]) return sshConnectionCache[connectionCacheKey];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
async function getSshTunnel(connection) {
|
||||
const sshConn = await getSshConnection(connection);
|
||||
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
|
||||
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));
|
||||
const tunnelConfig = {
|
||||
fromPort: localPort,
|
||||
toPort: connection.port,
|
||||
toHost: connection.server,
|
||||
};
|
||||
try {
|
||||
const tunnel = await sshConn.forward(tunnelConfig);
|
||||
console.log(
|
||||
`Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
|
||||
);
|
||||
|
||||
sshTunnelCache[tunnelCacheKey] = {
|
||||
state: 'ok',
|
||||
localPort,
|
||||
};
|
||||
return sshTunnelCache[tunnelCacheKey];
|
||||
} catch (err) {
|
||||
// error is not cached
|
||||
return {
|
||||
state: 'error',
|
||||
message: err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSshTunnel,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { getSshTunnel } = require('./sshTunnel');
|
||||
|
||||
const dispatchedMessages = {};
|
||||
|
||||
async function handleGetSshTunnelRequest({ msgid, connection }, subprocess) {
|
||||
const response = await getSshTunnel(connection);
|
||||
subprocess.send({ msgtype: 'getsshtunnel-response', msgid, response });
|
||||
}
|
||||
|
||||
function handleGetSshTunnelResponse({ msgid, response }, subprocess) {
|
||||
const { resolve } = dispatchedMessages[msgid];
|
||||
delete dispatchedMessages[msgid];
|
||||
resolve(response);
|
||||
}
|
||||
|
||||
async function getSshTunnelProxy(connection) {
|
||||
if (!process.send) return getSshTunnel(connection);
|
||||
const msgid = uuidv1();
|
||||
process.send({ msgtype: 'getsshtunnel-request', msgid, connection });
|
||||
return new Promise((resolve, reject) => {
|
||||
dispatchedMessages[msgid] = { resolve, reject };
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleGetSshTunnelRequest,
|
||||
handleGetSshTunnelResponse,
|
||||
getSshTunnelProxy,
|
||||
};
|
||||
@@ -8,24 +8,27 @@ var config = {
|
||||
app: './index.js',
|
||||
},
|
||||
target: 'node',
|
||||
node: {
|
||||
__dirname: false,
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
libraryTarget: 'commonjs2',
|
||||
},
|
||||
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
|
||||
// module: {
|
||||
// rules: [
|
||||
// {
|
||||
// test: /\.js$/,
|
||||
// exclude: /node_modules/
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.node$/,
|
||||
use: 'node-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.IgnorePlugin({
|
||||
checkResource(resource) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"version": "4.0.0",
|
||||
"name": "dbgate-datalib",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
@@ -12,11 +12,11 @@
|
||||
"lib"
|
||||
],
|
||||
"dependencies": {
|
||||
"dbgate-sqltree": "^1.0.0",
|
||||
"dbgate-filterparser": "^1.0.0"
|
||||
"dbgate-sqltree": "^4.0.0",
|
||||
"dbgate-filterparser": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dbgate-types": "^1.0.0",
|
||||
"dbgate-types": "^4.0.0",
|
||||
"@types/node": "^13.7.0",
|
||||
"typescript": "^3.7.5"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ChangeSetItem {
|
||||
pureName: string;
|
||||
schemaName?: string;
|
||||
insertedRowIndex?: number;
|
||||
document?: any;
|
||||
condition?: { [column: string]: string };
|
||||
fields?: { [column: string]: string };
|
||||
}
|
||||
@@ -45,7 +46,7 @@ export function findExistingChangeSetItem(
|
||||
return [
|
||||
'inserts',
|
||||
changeSet.inserts.find(
|
||||
(x) =>
|
||||
x =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
x.insertedRowIndex == definition.insertedRowIndex
|
||||
@@ -53,7 +54,7 @@ export function findExistingChangeSetItem(
|
||||
];
|
||||
} else {
|
||||
const inUpdates = changeSet.updates.find(
|
||||
(x) =>
|
||||
x =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
_.isEqual(x.condition, definition.condition)
|
||||
@@ -61,7 +62,7 @@ export function findExistingChangeSetItem(
|
||||
if (inUpdates) return ['updates', inUpdates];
|
||||
|
||||
const inDeletes = changeSet.deletes.find(
|
||||
(x) =>
|
||||
x =>
|
||||
x.pureName == definition.pureName &&
|
||||
x.schemaName == definition.schemaName &&
|
||||
_.isEqual(x.condition, definition.condition)
|
||||
@@ -86,7 +87,7 @@ export function setChangeSetValue(
|
||||
if (existingItem) {
|
||||
return {
|
||||
...changeSet,
|
||||
[fieldName]: changeSet[fieldName].map((item) =>
|
||||
[fieldName]: changeSet[fieldName].map(item =>
|
||||
item == existingItem
|
||||
? {
|
||||
...item,
|
||||
@@ -117,6 +118,43 @@ export function setChangeSetValue(
|
||||
};
|
||||
}
|
||||
|
||||
export function setChangeSetRowData(changeSet: ChangeSet, definition: ChangeSetRowDefinition, document: any): ChangeSet {
|
||||
if (!changeSet || !definition) return changeSet;
|
||||
let [fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
||||
if (fieldName == 'deletes') {
|
||||
changeSet = revertChangeSetRowChanges(changeSet, definition);
|
||||
[fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
||||
}
|
||||
if (existingItem) {
|
||||
return {
|
||||
...changeSet,
|
||||
[fieldName]: changeSet[fieldName].map(item =>
|
||||
item == existingItem
|
||||
? {
|
||||
...item,
|
||||
fields: {},
|
||||
document,
|
||||
}
|
||||
: item
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...changeSet,
|
||||
[fieldName]: [
|
||||
...changeSet[fieldName],
|
||||
{
|
||||
pureName: definition.pureName,
|
||||
schemaName: definition.schemaName,
|
||||
condition: definition.condition,
|
||||
insertedRowIndex: definition.insertedRowIndex,
|
||||
document,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// export function batchUpdateChangeSet(
|
||||
// changeSet: ChangeSet,
|
||||
// rowDefinitions: ChangeSetRowDefinition[],
|
||||
@@ -167,8 +205,8 @@ export function batchUpdateChangeSet(
|
||||
|
||||
function extractFields(item: ChangeSetItem, allowNulls = true): UpdateField[] {
|
||||
return _.keys(item.fields)
|
||||
.filter((targetColumn) => allowNulls || item.fields[targetColumn] != null)
|
||||
.map((targetColumn) => ({
|
||||
.filter(targetColumn => allowNulls || item.fields[targetColumn] != null)
|
||||
.map(targetColumn => ({
|
||||
targetColumn,
|
||||
exprType: 'value',
|
||||
value: item.fields[targetColumn],
|
||||
@@ -183,11 +221,11 @@ function insertToSql(
|
||||
if (fields.length == 0) return null;
|
||||
let autoInc = false;
|
||||
if (dbinfo) {
|
||||
const table = dbinfo.tables.find((x) => x.schemaName == item.schemaName && x.pureName == item.pureName);
|
||||
const table = dbinfo.tables.find(x => x.schemaName == item.schemaName && x.pureName == item.pureName);
|
||||
if (table) {
|
||||
const autoIncCol = table.columns.find((x) => x.autoIncrement);
|
||||
const autoIncCol = table.columns.find(x => x.autoIncrement);
|
||||
console.log('autoIncCol', autoIncCol);
|
||||
if (autoIncCol && fields.find((x) => x.targetColumn == autoIncCol.columnName)) {
|
||||
if (autoIncCol && fields.find(x => x.targetColumn == autoIncCol.columnName)) {
|
||||
autoInc = true;
|
||||
}
|
||||
}
|
||||
@@ -222,7 +260,7 @@ function insertToSql(
|
||||
function extractCondition(item: ChangeSetItem): Condition {
|
||||
return {
|
||||
conditionType: 'and',
|
||||
conditions: _.keys(item.condition).map((columnName) => ({
|
||||
conditions: _.keys(item.condition).map(columnName => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
@@ -273,7 +311,7 @@ function deleteToSql(item: ChangeSetItem): Delete {
|
||||
export function changeSetToSql(changeSet: ChangeSet, dbinfo: DatabaseInfo): Command[] {
|
||||
return _.compact(
|
||||
_.flatten([
|
||||
...changeSet.inserts.map((item) => insertToSql(item, dbinfo)) as any,
|
||||
...(changeSet.inserts.map(item => insertToSql(item, dbinfo)) as any),
|
||||
...changeSet.updates.map(updateToSql),
|
||||
...changeSet.deletes.map(deleteToSql),
|
||||
])
|
||||
@@ -289,7 +327,7 @@ export function revertChangeSetRowChanges(changeSet: ChangeSet, definition: Chan
|
||||
if (item)
|
||||
return {
|
||||
...changeSet,
|
||||
[field]: changeSet[field].filter((x) => x != item),
|
||||
[field]: changeSet[field].filter(x => x != item),
|
||||
};
|
||||
return changeSet;
|
||||
}
|
||||
@@ -321,8 +359,8 @@ export function deleteChangeSetRows(changeSet: ChangeSet, definition: ChangeSetR
|
||||
export function getChangeSetInsertedRows(changeSet: ChangeSet, name?: NamedObjectInfo) {
|
||||
if (!name) return [];
|
||||
if (!changeSet) return [];
|
||||
const rows = changeSet.inserts.filter((x) => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
||||
const maxIndex = _.maxBy(rows, (x) => x.insertedRowIndex)?.insertedRowIndex;
|
||||
const rows = changeSet.inserts.filter(x => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
||||
const maxIndex = _.maxBy(rows, x => x.insertedRowIndex)?.insertedRowIndex;
|
||||
if (maxIndex == null) return [];
|
||||
const res = Array(maxIndex + 1).fill({});
|
||||
for (const row of rows) {
|
||||
@@ -332,6 +370,7 @@ export function getChangeSetInsertedRows(changeSet: ChangeSet, name?: NamedObjec
|
||||
}
|
||||
|
||||
export function changeSetInsertNewRow(changeSet: ChangeSet, name?: NamedObjectInfo): ChangeSet {
|
||||
console.log('INSERT', name);
|
||||
const insertedRows = getChangeSetInsertedRows(changeSet, name);
|
||||
return {
|
||||
...changeSet,
|
||||
@@ -347,5 +386,6 @@ export function changeSetInsertNewRow(changeSet: ChangeSet, name?: NamedObjectIn
|
||||
}
|
||||
|
||||
export function changeSetContainsChanges(changeSet: ChangeSet) {
|
||||
if (!changeSet) return false;
|
||||
return changeSet.deletes.length > 0 || changeSet.updates.length > 0 || changeSet.inserts.length > 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import _ from 'lodash';
|
||||
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc, DisplayColumn } from './GridDisplay';
|
||||
import { EngineDriver, ViewInfo, ColumnInfo, CollectionInfo } from 'dbgate-types';
|
||||
import { GridConfig, GridCache } from './GridConfig';
|
||||
|
||||
function getObjectKeys(obj) {
|
||||
if (_.isArray(obj)) {
|
||||
return Object.keys(obj)
|
||||
.slice(0, 10)
|
||||
.map(x => parseInt(x));
|
||||
}
|
||||
if (_.isPlainObject(obj)) {
|
||||
return Object.keys(obj);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function createHeaderText(path) {
|
||||
let res = `${path[0]}`;
|
||||
for (let i = 1; i < path.length; i++) {
|
||||
const name = path[i];
|
||||
if (_.isNumber(name)) res += `[${name}]`;
|
||||
else res += `.${name}`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function getColumnsForObject(basePath, obj, res: any[], display) {
|
||||
for (const name of getObjectKeys(obj)) {
|
||||
const uniqueName = [...basePath, name].join('.');
|
||||
let column = res.find(x => x.uniqueName == uniqueName);
|
||||
if (!column) {
|
||||
column = getDisplayColumn(basePath, name, display);
|
||||
if (basePath.length > 0) {
|
||||
const lastIndex1 = _.findLastIndex(res, x => x.parentHeaderText.startsWith(column.parentHeaderText));
|
||||
const lastIndex2 = _.findLastIndex(res, x => x.headerText == column.parentHeaderText);
|
||||
// console.log(uniqueName, lastIndex1, lastIndex2);
|
||||
if (lastIndex1 >= 0) res.splice(lastIndex1 + 1, 0, column);
|
||||
else if (lastIndex2 >= 0) res.splice(lastIndex2 + 1, 0, column);
|
||||
else res.push(column);
|
||||
} else {
|
||||
res.push(column);
|
||||
}
|
||||
}
|
||||
if (_.isPlainObject(obj[name]) || _.isArray(obj[name])) {
|
||||
column.isExpandable = true;
|
||||
}
|
||||
|
||||
if (display.isExpandedColumn(column.uniqueName)) {
|
||||
getColumnsForObject([...basePath, name], obj[name], res, display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayColumn(basePath, columnName, display) {
|
||||
const uniquePath = [...basePath, columnName];
|
||||
const uniqueName = uniquePath.join('.');
|
||||
return {
|
||||
columnName,
|
||||
headerText: createHeaderText(uniquePath),
|
||||
uniqueName,
|
||||
uniquePath,
|
||||
isStructured: true,
|
||||
parentHeaderText: createHeaderText(basePath),
|
||||
filterType: 'mongo',
|
||||
pureName: display.collection?.pureName,
|
||||
schemaName: display.collection?.schemaName,
|
||||
};
|
||||
}
|
||||
|
||||
export function analyseCollectionDisplayColumns(rows, display) {
|
||||
const res = [];
|
||||
for (const row of rows || []) {
|
||||
getColumnsForObject([], row, res, display);
|
||||
}
|
||||
return (
|
||||
res.map(col => ({
|
||||
...col,
|
||||
isChecked: display.isColumnChecked(col),
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
|
||||
export class CollectionGridDisplay extends GridDisplay {
|
||||
constructor(
|
||||
public collection: CollectionInfo,
|
||||
driver: EngineDriver,
|
||||
config: GridConfig,
|
||||
setConfig: ChangeConfigFunc,
|
||||
cache: GridCache,
|
||||
setCache: ChangeCacheFunc,
|
||||
loadedRows
|
||||
) {
|
||||
super(config, setConfig, cache, setCache, driver);
|
||||
this.columns = analyseCollectionDisplayColumns(loadedRows, this);
|
||||
this.filterable = true;
|
||||
this.sortable = true;
|
||||
this.editable = true;
|
||||
this.supportsReload = true;
|
||||
this.isDynamicStructure = true;
|
||||
this.changeSetKeyFields = ['_id'];
|
||||
this.baseCollection = collection;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig';
|
||||
import { ForeignKeyInfo, TableInfo, ColumnInfo, EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
||||
import { parseFilter, getFilterType } from 'dbgate-filterparser';
|
||||
import { parseFilter, getFilterType, getFilterValueExpression } from 'dbgate-filterparser';
|
||||
import { filterName } from './filterName';
|
||||
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
||||
import { Expression, Select, treeToSql, dumpSqlSelect, Condition } from 'dbgate-sqltree';
|
||||
@@ -22,4 +22,99 @@ export class FormViewDisplay {
|
||||
public dbinfo: DatabaseInfo = null
|
||||
) {}
|
||||
|
||||
addFilterColumn(column) {
|
||||
if (!column) return;
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
formFilterColumns: [...(cfg.formFilterColumns || []), column.uniqueName],
|
||||
}));
|
||||
}
|
||||
|
||||
filterCellValue(column, rowData) {
|
||||
if (!column || !rowData) return;
|
||||
const value = rowData[column.uniqueName];
|
||||
const expr = getFilterValueExpression(value, column.dataType);
|
||||
if (expr) {
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: {
|
||||
...cfg.filters,
|
||||
[column.uniqueName]: expr,
|
||||
},
|
||||
addedColumns: cfg.addedColumns.includes(column.uniqueName)
|
||||
? cfg.addedColumns
|
||||
: [...cfg.addedColumns, column.uniqueName],
|
||||
}));
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
setFilter(uniqueName, value) {
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: {
|
||||
...cfg.filters,
|
||||
[uniqueName]: value,
|
||||
},
|
||||
}));
|
||||
this.reload();
|
||||
}
|
||||
|
||||
removeFilter(uniqueName) {
|
||||
const reloadRequired = !!this.config.filters[uniqueName];
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
formFilterColumns: (cfg.formFilterColumns || []).filter(x => x != uniqueName),
|
||||
filters: _.omit(cfg.filters || [], uniqueName),
|
||||
}));
|
||||
if (reloadRequired) this.reload();
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.setCache(cache => ({
|
||||
// ...cache,
|
||||
...createGridCache(),
|
||||
refreshTime: new Date().getTime(),
|
||||
}));
|
||||
}
|
||||
|
||||
getKeyValue(columnName) {
|
||||
const { formViewKey, formViewKeyRequested } = this.config;
|
||||
if (formViewKeyRequested && formViewKeyRequested[columnName]) return formViewKeyRequested[columnName];
|
||||
if (formViewKey && formViewKey[columnName]) return formViewKey[columnName];
|
||||
return null;
|
||||
}
|
||||
|
||||
requestKeyValue(columnName, value) {
|
||||
if (this.getKeyValue(columnName) == value) return;
|
||||
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
formViewKeyRequested: {
|
||||
...cfg.formViewKey,
|
||||
...cfg.formViewKeyRequested,
|
||||
[columnName]: value,
|
||||
},
|
||||
}));
|
||||
this.reload();
|
||||
}
|
||||
|
||||
extractKey(row) {
|
||||
if (!row || !this.baseTable || !this.baseTable.primaryKey) {
|
||||
return null;
|
||||
}
|
||||
const formViewKey = _.pick(
|
||||
row,
|
||||
this.baseTable.primaryKey.columns.map(x => x.columnName)
|
||||
);
|
||||
return formViewKey;
|
||||
}
|
||||
|
||||
cancelRequestKey(rowData) {
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
formViewKeyRequested: null,
|
||||
formViewKey: rowData ? this.extractKey(rowData) : cfg.formViewKey,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@ export class FreeTableGridDisplay extends GridDisplay {
|
||||
getDisplayColumns(model: FreeTableModel) {
|
||||
return (
|
||||
model?.structure?.columns
|
||||
?.map((col) => this.getDisplayColumn(col))
|
||||
?.map((col) => ({
|
||||
?.map(col => this.getDisplayColumn(col))
|
||||
?.map(col => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
|
||||
getDisplayColumn( col: ColumnInfo) {
|
||||
getDisplayColumn(col: ColumnInfo) {
|
||||
const uniquePath = [col.columnName];
|
||||
const uniqueName = uniquePath.join('.');
|
||||
return {
|
||||
|
||||
@@ -31,6 +31,8 @@ export interface GridConfig extends GridConfigColumns {
|
||||
reference?: GridReferenceDefinition;
|
||||
isFormView?: boolean;
|
||||
formViewKey?: { [uniqueName: string]: string };
|
||||
formViewKeyRequested?: { [uniqueName: string]: string };
|
||||
formFilterColumns: string[];
|
||||
}
|
||||
|
||||
export interface GridCache {
|
||||
@@ -47,6 +49,7 @@ export function createGridConfig(): GridConfig {
|
||||
sort: [],
|
||||
focusedColumn: null,
|
||||
grouping: {},
|
||||
formFilterColumns: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig';
|
||||
import { ForeignKeyInfo, TableInfo, ColumnInfo, EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
||||
import {
|
||||
ForeignKeyInfo,
|
||||
TableInfo,
|
||||
ColumnInfo,
|
||||
EngineDriver,
|
||||
NamedObjectInfo,
|
||||
DatabaseInfo,
|
||||
CollectionInfo,
|
||||
} from 'dbgate-types';
|
||||
import { parseFilter, getFilterType } from 'dbgate-filterparser';
|
||||
import { filterName } from './filterName';
|
||||
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
||||
@@ -18,9 +26,12 @@ export interface DisplayColumn {
|
||||
autoIncrement?: boolean;
|
||||
isPrimaryKey?: boolean;
|
||||
foreignKey?: ForeignKeyInfo;
|
||||
isExpandable?: boolean;
|
||||
isChecked?: boolean;
|
||||
hintColumnName?: string;
|
||||
dataType?: string;
|
||||
filterType?: boolean;
|
||||
isStructured?: boolean;
|
||||
}
|
||||
|
||||
export interface DisplayedColumnEx extends DisplayColumn {
|
||||
@@ -53,12 +64,17 @@ export abstract class GridDisplay {
|
||||
) {}
|
||||
columns: DisplayColumn[];
|
||||
baseTable?: TableInfo;
|
||||
baseCollection?: CollectionInfo;
|
||||
get baseTableOrCollection(): NamedObjectInfo {
|
||||
return this.baseTable || this.baseCollection;
|
||||
}
|
||||
changeSetKeyFields: string[] = null;
|
||||
sortable = false;
|
||||
filterable = false;
|
||||
editable = false;
|
||||
isLoadedCorrectly = true;
|
||||
supportsReload = false;
|
||||
isDynamicStructure = false;
|
||||
|
||||
setColumnVisibility(uniquePath: string[], isVisible: boolean) {
|
||||
const uniqueName = uniquePath.join('.');
|
||||
@@ -66,12 +82,12 @@ export abstract class GridDisplay {
|
||||
this.includeInColumnSet('hiddenColumns', uniqueName, !isVisible);
|
||||
} else {
|
||||
this.includeInColumnSet('addedColumns', uniqueName, isVisible);
|
||||
this.reload();
|
||||
if (!this.isDynamicStructure) this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
focusColumn(uniqueName: string) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
focusedColumn: uniqueName,
|
||||
}));
|
||||
@@ -90,48 +106,44 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
get allColumns() {
|
||||
return this.getColumns(null).filter((col) => col.isChecked || col.uniquePath.length == 1);
|
||||
return this.getColumns(null).filter(col => col.isChecked || col.uniquePath.length == 1);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.setCache((cache) => ({
|
||||
// ...cache,
|
||||
...createGridCache(),
|
||||
refreshTime: new Date().getTime(),
|
||||
}));
|
||||
this.setCache(reloadDataCacheFunc);
|
||||
}
|
||||
|
||||
includeInColumnSet(field: keyof GridConfigColumns, uniqueName: string, isIncluded: boolean) {
|
||||
// console.log('includeInColumnSet', field, uniqueName, isIncluded);
|
||||
if (isIncluded) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
[field]: [...(cfg[field] || []), uniqueName],
|
||||
}));
|
||||
} else {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
[field]: (cfg[field] || []).filter((x) => x != uniqueName),
|
||||
[field]: (cfg[field] || []).filter(x => x != uniqueName),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
showAllColumns() {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
hiddenColumns: [],
|
||||
}));
|
||||
}
|
||||
|
||||
hideAllColumns() {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
hiddenColumns: this.columns.map((x) => x.uniqueName),
|
||||
hiddenColumns: this.columns.map(x => x.uniqueName),
|
||||
}));
|
||||
}
|
||||
|
||||
get hiddenColumnIndexes() {
|
||||
return (this.config.hiddenColumns || []).map((x) => _.findIndex(this.columns, (y) => y.uniqueName == x));
|
||||
return (this.config.hiddenColumns || []).map(x => _.findIndex(this.columns, y => y.uniqueName == x));
|
||||
}
|
||||
|
||||
isColumnChecked(column: DisplayColumn) {
|
||||
@@ -179,10 +191,10 @@ export abstract class GridDisplay {
|
||||
applySortOnSelect(select: Select, displayedColumnInfo: DisplayedColumnInfo) {
|
||||
if (this.config.sort?.length > 0) {
|
||||
select.orderBy = this.config.sort
|
||||
.map((col) => ({ ...col, dispInfo: displayedColumnInfo[col.uniqueName] }))
|
||||
.map((col) => ({ ...col, expr: select.columns.find((x) => x.alias == col.uniqueName) }))
|
||||
.filter((col) => col.dispInfo && col.expr)
|
||||
.map((col) => ({
|
||||
.map(col => ({ ...col, dispInfo: displayedColumnInfo[col.uniqueName] }))
|
||||
.map(col => ({ ...col, expr: select.columns.find(x => x.alias == col.uniqueName) }))
|
||||
.filter(col => col.dispInfo && col.expr)
|
||||
.map(col => ({
|
||||
...col.expr,
|
||||
direction: col.order,
|
||||
}));
|
||||
@@ -194,16 +206,14 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
get groupColumns() {
|
||||
return this.isGrouped
|
||||
? _.keys(_.pickBy(this.config.grouping, (v) => v == 'GROUP' || v.startsWith('GROUP:')))
|
||||
: null;
|
||||
return this.isGrouped ? _.keys(_.pickBy(this.config.grouping, v => v == 'GROUP' || v.startsWith('GROUP:'))) : null;
|
||||
}
|
||||
|
||||
applyGroupOnSelect(select: Select, displayedColumnInfo: DisplayedColumnInfo) {
|
||||
const groupColumns = this.groupColumns;
|
||||
if (groupColumns && groupColumns.length > 0) {
|
||||
// @ts-ignore
|
||||
select.groupBy = groupColumns.map((col) => {
|
||||
select.groupBy = groupColumns.map(col => {
|
||||
const colExpr: Expression = {
|
||||
exprType: 'column',
|
||||
columnName: displayedColumnInfo[col].columnName,
|
||||
@@ -257,16 +267,16 @@ export abstract class GridDisplay {
|
||||
};
|
||||
}
|
||||
}
|
||||
select.columns = select.columns.filter((x) => x.alias);
|
||||
select.columns = select.columns.filter(x => x.alias);
|
||||
}
|
||||
}
|
||||
|
||||
getColumns(columnFilter) {
|
||||
return this.columns.filter((col) => filterName(columnFilter, col.columnName));
|
||||
return this.columns.filter(col => filterName(columnFilter, col.columnName));
|
||||
}
|
||||
|
||||
getGridColumns() {
|
||||
return this.getColumns(null).filter((x) => this.isColumnChecked(x));
|
||||
return this.getColumns(null).filter(x => this.isColumnChecked(x));
|
||||
}
|
||||
|
||||
isExpandedColumn(uniqueName: string) {
|
||||
@@ -282,7 +292,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
setFilter(uniqueName, value) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: {
|
||||
...cfg.filters,
|
||||
@@ -292,8 +302,30 @@ export abstract class GridDisplay {
|
||||
this.reload();
|
||||
}
|
||||
|
||||
showFilter(uniqueName) {
|
||||
this.setConfig(cfg => {
|
||||
if (!cfg.filters.uniqueName)
|
||||
return {
|
||||
...cfg,
|
||||
filters: {
|
||||
..._.omitBy(cfg.filters, v => !v),
|
||||
[uniqueName]: '',
|
||||
},
|
||||
};
|
||||
return cfg;
|
||||
});
|
||||
}
|
||||
|
||||
removeFilter(uniqueName) {
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: _.omit(cfg.filters, [uniqueName]),
|
||||
}));
|
||||
this.reload();
|
||||
}
|
||||
|
||||
setFilters(dct) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: {
|
||||
...cfg.filters,
|
||||
@@ -304,7 +336,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
setSort(uniqueName, order) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
sort: [{ uniqueName, order }],
|
||||
}));
|
||||
@@ -312,7 +344,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
setGrouping(uniqueName, groupFunc: GroupFunc) {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
grouping: groupFunc
|
||||
? {
|
||||
@@ -327,7 +359,7 @@ export abstract class GridDisplay {
|
||||
getGrouping(uniqueName): GroupFunc {
|
||||
if (this.isGrouped) {
|
||||
if (this.config.grouping[uniqueName]) return this.config.grouping[uniqueName];
|
||||
const column = this.baseTable.columns.find((x) => x.columnName == uniqueName);
|
||||
const column = this.baseTable.columns.find(x => x.columnName == uniqueName);
|
||||
if (isTypeLogical(column?.dataType)) return 'COUNT DISTINCT';
|
||||
if (column?.autoIncrement) return 'COUNT';
|
||||
return 'MAX';
|
||||
@@ -336,7 +368,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
clearGrouping() {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
grouping: {},
|
||||
}));
|
||||
@@ -344,7 +376,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
getSortOrder(uniqueName) {
|
||||
return this.config.sort.find((x) => x.uniqueName == uniqueName)?.order;
|
||||
return this.config.sort.find(x => x.uniqueName == uniqueName)?.order;
|
||||
}
|
||||
|
||||
get filterCount() {
|
||||
@@ -352,7 +384,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
clearFilters() {
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
filters: {},
|
||||
}));
|
||||
@@ -365,10 +397,14 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
getChangeSetField(row, uniqueName, insertedRowIndex): ChangeSetFieldDefinition {
|
||||
const col = this.columns.find((x) => x.uniqueName == uniqueName);
|
||||
const col = this.columns.find(x => x.uniqueName == uniqueName);
|
||||
if (!col) return null;
|
||||
if (!this.baseTable) return null;
|
||||
if (this.baseTable.pureName != col.pureName || this.baseTable.schemaName != col.schemaName) return null;
|
||||
const baseTableOrCollection = this.baseTableOrCollection;
|
||||
if (!baseTableOrCollection) return null;
|
||||
if (baseTableOrCollection.pureName != col.pureName || baseTableOrCollection.schemaName != col.schemaName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...this.getChangeSetRow(row, insertedRowIndex),
|
||||
uniqueName: uniqueName,
|
||||
@@ -377,10 +413,11 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
getChangeSetRow(row, insertedRowIndex): ChangeSetRowDefinition {
|
||||
if (!this.baseTable) return null;
|
||||
const baseTableOrCollection = this.baseTableOrCollection;
|
||||
if (!baseTableOrCollection) return null;
|
||||
return {
|
||||
pureName: this.baseTable.pureName,
|
||||
schemaName: this.baseTable.schemaName,
|
||||
pureName: baseTableOrCollection.pureName,
|
||||
schemaName: baseTableOrCollection.schemaName,
|
||||
insertedRowIndex,
|
||||
condition: insertedRowIndex == null ? this.getChangeSetCondition(row) : null,
|
||||
};
|
||||
@@ -398,7 +435,7 @@ export abstract class GridDisplay {
|
||||
const select: Select = {
|
||||
commandType: 'select',
|
||||
from: { name, alias: 'basetbl' },
|
||||
columns: columns.map((col) => ({
|
||||
columns: columns.map(col => ({
|
||||
exprType: 'column',
|
||||
alias: col.columnName,
|
||||
source: { alias: 'basetbl' },
|
||||
@@ -413,7 +450,7 @@ export abstract class GridDisplay {
|
||||
],
|
||||
};
|
||||
const displayedColumnInfo = _.keyBy(
|
||||
this.columns.map((col) => ({ ...col, sourceAlias: 'basetbl' })),
|
||||
this.columns.map(col => ({ ...col, sourceAlias: 'basetbl' })),
|
||||
'uniqueName'
|
||||
);
|
||||
this.processReferences(select, displayedColumnInfo, options);
|
||||
@@ -442,7 +479,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
resizeColumn(uniqueName: string, computedSize: number, diff: number) {
|
||||
this.setConfig((cfg) => {
|
||||
this.setConfig(cfg => {
|
||||
const columnWidths = {
|
||||
...cfg.columnWidths,
|
||||
};
|
||||
@@ -495,7 +532,7 @@ export abstract class GridDisplay {
|
||||
if (!filters) return null;
|
||||
const conditions = [];
|
||||
for (const name in filters) {
|
||||
const column = this.columns.find((x) => (x.columnName = name));
|
||||
const column = this.columns.find(x => (x.columnName = name));
|
||||
if (!column) continue;
|
||||
const filterType = getFilterType(column.dataType);
|
||||
try {
|
||||
@@ -525,13 +562,31 @@ export abstract class GridDisplay {
|
||||
if (!primaryKey) return;
|
||||
const { columns } = primaryKey;
|
||||
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
isFormView: true,
|
||||
formViewKey: _.pick(
|
||||
rowData,
|
||||
columns.map((x) => x.columnName)
|
||||
),
|
||||
formViewKey: rowData
|
||||
? _.pick(
|
||||
rowData,
|
||||
columns.map(x => x.columnName)
|
||||
)
|
||||
: null,
|
||||
formViewKeyRequested: null,
|
||||
}));
|
||||
}
|
||||
|
||||
switchToJsonView() {
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
isJsonView: true,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export function reloadDataCacheFunc(cache: GridCache): GridCache {
|
||||
return {
|
||||
// ...cache,
|
||||
...createGridCache(),
|
||||
refreshTime: new Date().getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,34 +1,44 @@
|
||||
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
|
||||
import { QueryResultColumn } from 'dbgate-types';
|
||||
import { GridConfig, GridCache } from './GridConfig';
|
||||
import { analyseCollectionDisplayColumns } from './CollectionGridDisplay';
|
||||
|
||||
export class JslGridDisplay extends GridDisplay {
|
||||
constructor(
|
||||
jslid,
|
||||
columns: QueryResultColumn[],
|
||||
structure,
|
||||
config: GridConfig,
|
||||
setConfig: ChangeConfigFunc,
|
||||
cache: GridCache,
|
||||
setCache: ChangeCacheFunc
|
||||
setCache: ChangeCacheFunc,
|
||||
rows: any
|
||||
) {
|
||||
super(config, setConfig, cache, setCache, null);
|
||||
|
||||
this.filterable = true;
|
||||
|
||||
this.columns = columns
|
||||
.map((col) => ({
|
||||
columnName: col.columnName,
|
||||
headerText: col.columnName,
|
||||
uniqueName: col.columnName,
|
||||
uniquePath: [col.columnName],
|
||||
notNull: col.notNull,
|
||||
autoIncrement: col.autoIncrement,
|
||||
pureName: null,
|
||||
schemaName: null,
|
||||
}))
|
||||
?.map((col) => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
}));
|
||||
if (structure.columns) {
|
||||
this.columns = structure.columns
|
||||
.map(col => ({
|
||||
columnName: col.columnName,
|
||||
headerText: col.columnName,
|
||||
uniqueName: col.columnName,
|
||||
uniquePath: [col.columnName],
|
||||
notNull: col.notNull,
|
||||
autoIncrement: col.autoIncrement,
|
||||
pureName: null,
|
||||
schemaName: null,
|
||||
}))
|
||||
?.map(col => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
}));
|
||||
}
|
||||
|
||||
if (structure.__isDynamicStructure) {
|
||||
this.columns = analyseCollectionDisplayColumns(rows, this);
|
||||
}
|
||||
|
||||
if (!this.columns) this.columns = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,6 @@ export interface MacroDefinition {
|
||||
export interface MacroSelectedCell {
|
||||
column: string;
|
||||
row: number;
|
||||
rowData: any;
|
||||
value: any;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import stableStringify from 'json-stable-stringify';
|
||||
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
||||
|
||||
export class TableFormViewDisplay extends FormViewDisplay {
|
||||
// use utility functions from GridDisplay and publish result in FromViewDisplat interface
|
||||
// use utility functions from GridDisplay and publish result in FromViewDisplay interface
|
||||
private gridDisplay: TableGridDisplay;
|
||||
|
||||
constructor(
|
||||
@@ -32,14 +32,30 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
||||
) {
|
||||
super(config, setConfig, cache, setCache, driver, dbinfo);
|
||||
this.gridDisplay = new TableGridDisplay(tableName, driver, config, setConfig, cache, setCache, dbinfo);
|
||||
this.gridDisplay.addAllExpandedColumnsToSelected = true;
|
||||
|
||||
this.isLoadedCorrectly = this.gridDisplay.isLoadedCorrectly;
|
||||
this.columns = this.gridDisplay.columns;
|
||||
this.isLoadedCorrectly = this.gridDisplay.isLoadedCorrectly && !!this.driver;
|
||||
this.columns = [];
|
||||
this.addDisplayColumns(this.gridDisplay.columns);
|
||||
this.baseTable = this.gridDisplay.baseTable;
|
||||
this.gridDisplay.hintBaseColumns = this.columns;
|
||||
}
|
||||
|
||||
addDisplayColumns(columns: DisplayColumn[]) {
|
||||
for (const col of columns) {
|
||||
this.columns.push(col);
|
||||
if (this.gridDisplay.isExpandedColumn(col.uniqueName)) {
|
||||
const table = this.gridDisplay.getFkTarget(col);
|
||||
if (table) {
|
||||
const subcolumns = this.gridDisplay.getDisplayColumns(table, col.uniquePath);
|
||||
this.addDisplayColumns(subcolumns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getPrimaryKeyEqualCondition(row = null): Condition {
|
||||
if (!row) row = this.config.formViewKey;
|
||||
if (!row) row = this.config.formViewKeyRequested || this.config.formViewKey;
|
||||
if (!row) return null;
|
||||
const { primaryKey } = this.gridDisplay.baseTable;
|
||||
if (!primaryKey) return null;
|
||||
@@ -57,7 +73,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
||||
},
|
||||
right: {
|
||||
exprType: 'value',
|
||||
value: this.config.formViewKey[columnName],
|
||||
value: row[columnName],
|
||||
},
|
||||
})),
|
||||
};
|
||||
@@ -166,27 +182,15 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
||||
return sql;
|
||||
}
|
||||
|
||||
extractKey(row) {
|
||||
if (!row || !this.gridDisplay.baseTable || !this.gridDisplay.baseTable.primaryKey) {
|
||||
return null;
|
||||
}
|
||||
const formViewKey = _.pick(
|
||||
row,
|
||||
this.gridDisplay.baseTable.primaryKey.columns.map((x) => x.columnName)
|
||||
);
|
||||
return formViewKey;
|
||||
}
|
||||
|
||||
navigate(row) {
|
||||
const formViewKey = this.extractKey(row);
|
||||
this.setConfig((cfg) => ({
|
||||
this.setConfig(cfg => ({
|
||||
...cfg,
|
||||
formViewKey,
|
||||
}));
|
||||
}
|
||||
|
||||
isLoadedCurrentRow(row) {
|
||||
console.log('isLoadedCurrentRow', row, this.config.formViewKey);
|
||||
if (!row) return false;
|
||||
const formViewKey = this.extractKey(row);
|
||||
return stableStringify(formViewKey) == stableStringify(this.config.formViewKey);
|
||||
@@ -238,7 +242,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
||||
}
|
||||
|
||||
getChangeSetField(row, uniqueName): ChangeSetFieldDefinition {
|
||||
const col = this.columns.find((x) => x.uniqueName == uniqueName);
|
||||
const col = this.columns.find(x => x.uniqueName == uniqueName);
|
||||
if (!col) return null;
|
||||
if (!this.baseTable) return null;
|
||||
if (this.baseTable.pureName != col.pureName || this.baseTable.schemaName != col.schemaName) return null;
|
||||
@@ -248,4 +252,13 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
||||
columnName: col.columnName,
|
||||
};
|
||||
}
|
||||
|
||||
toggleExpandedColumn(uniqueName: string) {
|
||||
this.gridDisplay.toggleExpandedColumn(uniqueName);
|
||||
this.gridDisplay.reload();
|
||||
}
|
||||
|
||||
isExpandedColumn(uniqueName: string) {
|
||||
return this.gridDisplay.isExpandedColumn(uniqueName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { filterName } from './filterName';
|
||||
|
||||
export class TableGridDisplay extends GridDisplay {
|
||||
public table: TableInfo;
|
||||
public addAllExpandedColumnsToSelected = false;
|
||||
public hintBaseColumns: DisplayColumn[];
|
||||
|
||||
constructor(
|
||||
public tableName: NamedObjectInfo,
|
||||
@@ -36,8 +38,8 @@ export class TableGridDisplay extends GridDisplay {
|
||||
this.baseTable = this.table;
|
||||
if (this.table && this.table.columns) {
|
||||
this.changeSetKeyFields = this.table.primaryKey
|
||||
? this.table.primaryKey.columns.map((x) => x.columnName)
|
||||
: this.table.columns.map((x) => x.columnName);
|
||||
? this.table.primaryKey.columns.map(x => x.columnName)
|
||||
: this.table.columns.map(x => x.columnName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +47,19 @@ export class TableGridDisplay extends GridDisplay {
|
||||
return (
|
||||
this.dbinfo &&
|
||||
this.dbinfo.tables &&
|
||||
this.dbinfo.tables.find((x) => x.pureName == pureName && x.schemaName == schemaName)
|
||||
this.dbinfo.tables.find(x => x.pureName == pureName && x.schemaName == schemaName)
|
||||
);
|
||||
}
|
||||
|
||||
getDisplayColumns(table: TableInfo, parentPath: string[]) {
|
||||
return (
|
||||
table?.columns
|
||||
?.map((col) => this.getDisplayColumn(table, col, parentPath))
|
||||
?.map((col) => ({
|
||||
?.map(col => this.getDisplayColumn(table, col, parentPath))
|
||||
?.map(col => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
hintColumnName: col.foreignKey ? `hint_${col.uniqueName}` : null,
|
||||
isExpandable: !!col.foreignKey,
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
@@ -80,7 +83,7 @@ export class TableGridDisplay extends GridDisplay {
|
||||
|
||||
addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) {
|
||||
const childAlias = `${column.uniqueName}_ref`;
|
||||
if ((select.from.relations || []).find((x) => x.alias == childAlias)) return;
|
||||
if ((select.from.relations || []).find(x => x.alias == childAlias)) return;
|
||||
const table = this.getFkTarget(column);
|
||||
if (table && table.primaryKey) {
|
||||
select.from.relations = [
|
||||
@@ -113,14 +116,14 @@ export class TableGridDisplay extends GridDisplay {
|
||||
addHintsToSelect(select: Select): boolean {
|
||||
let res = false;
|
||||
const groupColumns = this.groupColumns;
|
||||
for (const column of this.getGridColumns()) {
|
||||
for (const column of this.hintBaseColumns || this.getGridColumns()) {
|
||||
if (column.foreignKey) {
|
||||
if (groupColumns && !groupColumns.includes(column.uniqueName)) {
|
||||
continue;
|
||||
}
|
||||
const table = this.getFkTarget(column);
|
||||
if (table && table.columns && table.columns.length > 0 && table.primaryKey) {
|
||||
const hintColumn = table.columns.find((x) => x?.dataType?.toLowerCase()?.includes('char'));
|
||||
const hintColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char'));
|
||||
if (hintColumn) {
|
||||
const parentUniqueName = column.uniquePath.slice(0, -1).join('.');
|
||||
this.addReferenceToSelect(select, parentUniqueName ? `${parentUniqueName}_ref` : 'basetbl', column);
|
||||
@@ -177,7 +180,7 @@ export class TableGridDisplay extends GridDisplay {
|
||||
}
|
||||
|
||||
getColumns(columnFilter) {
|
||||
return this.enrichExpandedColumns(this.columns.filter((col) => filterName(columnFilter, col.columnName)));
|
||||
return this.enrichExpandedColumns(this.columns.filter(col => filterName(columnFilter, col.columnName)));
|
||||
}
|
||||
|
||||
getDisplayColumn(table: TableInfo, col: ColumnInfo, parentPath: string[]) {
|
||||
@@ -191,10 +194,10 @@ export class TableGridDisplay extends GridDisplay {
|
||||
headerText: uniquePath.length == 1 ? col.columnName : `${table.pureName}.${col.columnName}`,
|
||||
uniqueName,
|
||||
uniquePath,
|
||||
isPrimaryKey: table.primaryKey && !!table.primaryKey.columns.find((x) => x.columnName == col.columnName),
|
||||
isPrimaryKey: table.primaryKey && !!table.primaryKey.columns.find(x => x.columnName == col.columnName),
|
||||
foreignKey:
|
||||
table.foreignKeys &&
|
||||
table.foreignKeys.find((fk) => fk.columns.length == 1 && fk.columns[0].columnName == col.columnName),
|
||||
table.foreignKeys.find(fk => fk.columns.length == 1 && fk.columns[0].columnName == col.columnName),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -205,7 +208,7 @@ export class TableGridDisplay extends GridDisplay {
|
||||
displayedColumnInfo: DisplayedColumnInfo
|
||||
) {
|
||||
for (const column of columns) {
|
||||
if (this.config.addedColumns.includes(column.uniqueName)) {
|
||||
if (this.addAllExpandedColumnsToSelected || this.config.addedColumns.includes(column.uniqueName)) {
|
||||
select.columns.push({
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
|
||||
@@ -23,8 +23,8 @@ export class ViewGridDisplay extends GridDisplay {
|
||||
getDisplayColumns(view: ViewInfo) {
|
||||
return (
|
||||
view?.columns
|
||||
?.map((col) => this.getDisplayColumn(view, col))
|
||||
?.map((col) => ({
|
||||
?.map(col => this.getDisplayColumn(view, col))
|
||||
?.map(col => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
})) || []
|
||||
|
||||
@@ -11,3 +11,4 @@ export * from './MacroDefinition';
|
||||
export * from './runMacro';
|
||||
export * from './FormViewDisplay';
|
||||
export * from './TableFormViewDisplay';
|
||||
export * from './CollectionGridDisplay';
|
||||
|
||||
@@ -4,19 +4,21 @@ import uuidv1 from 'uuid/v1';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import moment from 'moment';
|
||||
import { MacroDefinition, MacroSelectedCell } from './MacroDefinition';
|
||||
import { ChangeSet, setChangeSetValue } from './ChangeSet';
|
||||
import { GridDisplay } from './GridDisplay';
|
||||
|
||||
const getMacroFunction = {
|
||||
transformValue: (code) => `
|
||||
transformValue: code => `
|
||||
(value, args, modules, rowIndex, row, columnName) => {
|
||||
${code}
|
||||
}
|
||||
`,
|
||||
transformRows: (code) => `
|
||||
transformRows: code => `
|
||||
(rows, args, modules, selectedCells, cols, columns) => {
|
||||
${code}
|
||||
}
|
||||
`,
|
||||
transformData: (code) => `
|
||||
transformData: code => `
|
||||
(rows, args, modules, selectedCells, cols, columns) => {
|
||||
${code}
|
||||
}
|
||||
@@ -83,8 +85,8 @@ function runTramsformValue(
|
||||
}
|
||||
|
||||
function removePreviewRowFlags(rows) {
|
||||
rows = rows.filter((row) => row.__rowStatus != 'deleted');
|
||||
rows = rows.map((row) => {
|
||||
rows = rows.filter(row => row.__rowStatus != 'deleted');
|
||||
rows = rows.map(row => {
|
||||
if (row.__rowStatus || row.__modifiedFields || row.__insertedFields || row.__deletedFields)
|
||||
return _.omit(row, ['__rowStatus', '__modifiedFields', '__insertedFields', '__deletedFields']);
|
||||
return row;
|
||||
@@ -107,7 +109,7 @@ function runTramsformRows(
|
||||
macroArgs,
|
||||
modules,
|
||||
selectedCells,
|
||||
data.structure.columns.map((x) => x.columnName),
|
||||
data.structure.columns.map(x => x.columnName),
|
||||
data.structure.columns
|
||||
);
|
||||
if (!preview) {
|
||||
@@ -136,11 +138,11 @@ function runTramsformData(
|
||||
macroArgs,
|
||||
modules,
|
||||
selectedCells,
|
||||
data.structure.columns.map((x) => x.columnName),
|
||||
data.structure.columns.map(x => x.columnName),
|
||||
data.structure.columns
|
||||
);
|
||||
if (cols && !columns) {
|
||||
columns = cols.map((columnName) => ({ columnName }));
|
||||
columns = cols.map(columnName => ({ columnName }));
|
||||
}
|
||||
columns = _.uniqBy(columns, 'columnName');
|
||||
if (!preview) {
|
||||
@@ -183,3 +185,55 @@ export function runMacro(
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export function compileMacroFunction(macro: MacroDefinition, errors = []) {
|
||||
if (!macro) return null;
|
||||
let func;
|
||||
try {
|
||||
func = eval(getMacroFunction[macro.type](macro.code));
|
||||
return func;
|
||||
} catch (err) {
|
||||
errors.push(`Error compiling macro ${macro.name}: ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function runMacroOnValue(compiledFunc, macroArgs, value, rowIndex, row, column, errors = []) {
|
||||
if (!compiledFunc) return value;
|
||||
try {
|
||||
const res = compiledFunc(value, macroArgs, modules, rowIndex, row, column);
|
||||
return res;
|
||||
} catch (err) {
|
||||
errors.push(`Error processing column ${column} on row ${rowIndex}: ${err.message}`);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function runMacroOnChangeSet(
|
||||
macro: MacroDefinition,
|
||||
macroArgs: {},
|
||||
selectedCells: MacroSelectedCell[],
|
||||
changeSet: ChangeSet,
|
||||
display: GridDisplay
|
||||
): ChangeSet {
|
||||
const errors = [];
|
||||
const compiledMacroFunc = compileMacroFunction(macro, errors);
|
||||
if (!compiledMacroFunc) return null;
|
||||
|
||||
let res = changeSet;
|
||||
for (const cell of selectedCells) {
|
||||
const definition = display.getChangeSetField(cell.rowData, cell.column, undefined);
|
||||
const macroResult = runMacroOnValue(
|
||||
compiledMacroFunc,
|
||||
macroArgs,
|
||||
cell.value,
|
||||
cell.row,
|
||||
cell.rowData,
|
||||
cell.column,
|
||||
errors
|
||||
);
|
||||
res = setChangeSetValue(res, definition, macroResult);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](https://paypal.me/JanProchazkaCz/30eur)
|
||||
[](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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||

|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const dbgateApi = require('dbgate-api');
|
||||
|
||||
global.dbgateApiModulePath = require.resolve('dbgate-api');
|
||||
|
||||
dbgateApi.getMainModule().start('startNodeWeb');
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "dbgate",
|
||||
"version": "4.0.0",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur",
|
||||
"description": "Opensource database administration tool - web interface",
|
||||
"author": "Jan Prochazka",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"dbgate": "./bin/dbgate.js"
|
||||
},
|
||||
"keywords": [
|
||||
"sql",
|
||||
"dbgate",
|
||||
"web"
|
||||
],
|
||||
"dependencies": {
|
||||
"dbgate-api": "^4.0.0",
|
||||
"dbgate-web": "^4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"version": "4.0.0",
|
||||
"name": "dbgate-filterparser",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
@@ -13,7 +13,7 @@
|
||||
"lib"
|
||||
],
|
||||
"devDependencies": {
|
||||
"dbgate-types": "^1.0.0",
|
||||
"dbgate-types": "^4.0.0",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/node": "^13.7.0",
|
||||
"jest": "^24.9.0",
|
||||
@@ -22,9 +22,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/parsimmon": "^1.10.1",
|
||||
"dbgate-tools": "^1.0.0",
|
||||
"dbgate-tools": "^4.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"moment": "^2.24.0",
|
||||
"parsimmon": "^1.13.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import P from 'parsimmon';
|
||||
|
||||
export const whitespace = P.regexp(/\s*/m);
|
||||
|
||||
export function token(parser) {
|
||||
return parser.skip(whitespace);
|
||||
}
|
||||
|
||||
export function word(str) {
|
||||
return P.string(str).thru(token);
|
||||
}
|
||||
|
||||
export function interpretEscapes(str) {
|
||||
let escapes = {
|
||||
b: '\b',
|
||||
f: '\f',
|
||||
n: '\n',
|
||||
r: '\r',
|
||||
t: '\t',
|
||||
};
|
||||
return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => {
|
||||
let type = escape.charAt(0);
|
||||
let hex = escape.slice(1);
|
||||
if (type === 'u') {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
if (escapes.hasOwnProperty(type)) {
|
||||
return escapes[type];
|
||||
}
|
||||
return type;
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { isTypeDateTime } from 'dbgate-tools';
|
||||
import moment from 'moment';
|
||||
|
||||
export type FilterMultipleValuesMode = 'is' | 'is_not' | 'contains' | 'begins' | 'ends';
|
||||
|
||||
export function getFilterValueExpression(value, dataType) {
|
||||
if (value == null) return 'NULL';
|
||||
if (isTypeDateTime(dataType)) return value;
|
||||
if (isTypeDateTime(dataType)) return moment(value).format('YYYY-MM-DD HH:mm:ss');
|
||||
return `="${value}"`;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import P from 'parsimmon';
|
||||
import { interpretEscapes, token, word, whitespace } from './common';
|
||||
|
||||
const operatorCondition = operator => value => ({
|
||||
__placeholder__: {
|
||||
[operator]: value,
|
||||
},
|
||||
});
|
||||
|
||||
const regexCondition = regexString => value => ({
|
||||
__placeholder__: {
|
||||
$regex: regexString.replace('#VALUE#', value),
|
||||
$options: 'i',
|
||||
},
|
||||
});
|
||||
|
||||
const numberTestCondition = () => value => ({
|
||||
$or: [
|
||||
{
|
||||
__placeholder__: {
|
||||
$regex: `.*${value}.*`,
|
||||
$options: 'i',
|
||||
},
|
||||
},
|
||||
{
|
||||
__placeholder__: value,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const testCondition = (operator, value) => () => ({
|
||||
__placeholder__: {
|
||||
[operator]: value,
|
||||
},
|
||||
});
|
||||
|
||||
const compoudCondition = conditionType => conditions => {
|
||||
if (conditions.length == 1) return conditions[0];
|
||||
return {
|
||||
[conditionType]: conditions,
|
||||
};
|
||||
};
|
||||
|
||||
const negateCondition = condition => ({
|
||||
__placeholder__: {
|
||||
$not: condition.__placeholder__,
|
||||
},
|
||||
});
|
||||
|
||||
const createParser = () => {
|
||||
const langDef = {
|
||||
string1: () =>
|
||||
token(P.regexp(/"((?:\\.|.)*?)"/, 1))
|
||||
.map(interpretEscapes)
|
||||
.desc('string quoted'),
|
||||
|
||||
string2: () =>
|
||||
token(P.regexp(/'((?:\\.|.)*?)'/, 1))
|
||||
.map(interpretEscapes)
|
||||
.desc('string quoted'),
|
||||
|
||||
number: () =>
|
||||
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
|
||||
.map(Number)
|
||||
.desc('number'),
|
||||
|
||||
noQuotedString: () => P.regexp(/[^\s^,^'^"]+/).desc('string unquoted'),
|
||||
|
||||
value: r => P.alt(r.string1, r.string2, r.number, r.noQuotedString),
|
||||
valueTestNum: r => r.number.map(numberTestCondition()),
|
||||
valueTest: r => r.value.map(regexCondition('.*#VALUE#.*')),
|
||||
|
||||
comma: () => word(','),
|
||||
not: () => word('NOT'),
|
||||
notExists: r => r.not.then(r.exists).map(testCondition('$exists', false)),
|
||||
exists: () => word('EXISTS').map(testCondition('$exists', true)),
|
||||
true: () => word('TRUE').map(testCondition('$eq', true)),
|
||||
false: () => word('FALSE').map(testCondition('$eq', false)),
|
||||
|
||||
eq: r => word('=').then(r.value).map(operatorCondition('$eq')),
|
||||
ne: r => word('!=').then(r.value).map(operatorCondition('$ne')),
|
||||
lt: r => word('<').then(r.value).map(operatorCondition('$lt')),
|
||||
gt: r => word('>').then(r.value).map(operatorCondition('$gt')),
|
||||
le: r => word('<=').then(r.value).map(operatorCondition('$lte')),
|
||||
ge: r => word('>=').then(r.value).map(operatorCondition('$gte')),
|
||||
startsWith: r => word('^').then(r.value).map(regexCondition('#VALUE#.*')),
|
||||
endsWith: r => word('$').then(r.value).map(regexCondition('.*#VALUE#')),
|
||||
contains: r => word('+').then(r.value).map(regexCondition('.*#VALUE#.*')),
|
||||
startsWithNot: r => word('!^').then(r.value).map(regexCondition('#VALUE#.*')).map(negateCondition),
|
||||
endsWithNot: r => word('!$').then(r.value).map(regexCondition('.*#VALUE#')).map(negateCondition),
|
||||
containsNot: r => word('~').then(r.value).map(regexCondition('.*#VALUE#.*')).map(negateCondition),
|
||||
|
||||
element: r =>
|
||||
P.alt(
|
||||
r.exists,
|
||||
r.notExists,
|
||||
r.true,
|
||||
r.false,
|
||||
r.eq,
|
||||
r.ne,
|
||||
r.lt,
|
||||
r.gt,
|
||||
r.le,
|
||||
r.ge,
|
||||
r.startsWith,
|
||||
r.endsWith,
|
||||
r.contains,
|
||||
r.startsWithNot,
|
||||
r.endsWithNot,
|
||||
r.containsNot,
|
||||
r.valueTestNum,
|
||||
r.valueTest
|
||||
).trim(whitespace),
|
||||
factor: r => r.element.sepBy(whitespace).map(compoudCondition('$and')),
|
||||
list: r => r.factor.sepBy(r.comma).map(compoudCondition('$or')),
|
||||
};
|
||||
|
||||
return P.createLanguage(langDef);
|
||||
};
|
||||
|
||||
export const mongoParser = createParser();
|
||||
@@ -1,40 +1,12 @@
|
||||
import P from 'parsimmon';
|
||||
import moment from 'moment';
|
||||
import { FilterType } from './types';
|
||||
import { Condition } from 'dbgate-sqltree';
|
||||
import { TransformType } from 'dbgate-types';
|
||||
import { interpretEscapes, token, word, whitespace } from './common';
|
||||
import { mongoParser } from './mongoParser';
|
||||
|
||||
const whitespace = P.regexp(/\s*/m);
|
||||
|
||||
function token(parser) {
|
||||
return parser.skip(whitespace);
|
||||
}
|
||||
|
||||
function word(str) {
|
||||
return P.string(str).thru(token);
|
||||
}
|
||||
|
||||
function interpretEscapes(str) {
|
||||
let escapes = {
|
||||
b: '\b',
|
||||
f: '\f',
|
||||
n: '\n',
|
||||
r: '\r',
|
||||
t: '\t',
|
||||
};
|
||||
return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => {
|
||||
let type = escape.charAt(0);
|
||||
let hex = escape.slice(1);
|
||||
if (type === 'u') {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
if (escapes.hasOwnProperty(type)) {
|
||||
return escapes[type];
|
||||
}
|
||||
return type;
|
||||
});
|
||||
}
|
||||
|
||||
const binaryCondition = (operator) => (value) => ({
|
||||
const binaryCondition = operator => value => ({
|
||||
conditionType: 'binary',
|
||||
operator,
|
||||
left: {
|
||||
@@ -46,7 +18,7 @@ const binaryCondition = (operator) => (value) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const likeCondition = (conditionType, likeString) => (value) => ({
|
||||
const likeCondition = (conditionType, likeString) => value => ({
|
||||
conditionType,
|
||||
left: {
|
||||
exprType: 'placeholder',
|
||||
@@ -57,7 +29,7 @@ const likeCondition = (conditionType, likeString) => (value) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const compoudCondition = (conditionType) => (conditions) => {
|
||||
const compoudCondition = conditionType => conditions => {
|
||||
if (conditions.length == 1) return conditions[0];
|
||||
return {
|
||||
conditionType,
|
||||
@@ -65,7 +37,7 @@ const compoudCondition = (conditionType) => (conditions) => {
|
||||
};
|
||||
};
|
||||
|
||||
const unaryCondition = (conditionType) => () => {
|
||||
const unaryCondition = conditionType => () => {
|
||||
return {
|
||||
conditionType,
|
||||
expr: {
|
||||
@@ -74,7 +46,7 @@ const unaryCondition = (conditionType) => () => {
|
||||
};
|
||||
};
|
||||
|
||||
const binaryFixedValueCondition = (value) => () => {
|
||||
const binaryFixedValueCondition = value => () => {
|
||||
return {
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
@@ -88,7 +60,7 @@ const binaryFixedValueCondition = (value) => () => {
|
||||
};
|
||||
};
|
||||
|
||||
const negateCondition = (condition) => {
|
||||
const negateCondition = condition => {
|
||||
return {
|
||||
conditionType: 'not',
|
||||
condition,
|
||||
@@ -113,11 +85,11 @@ function getTransformCondition(transform: TransformType, value) {
|
||||
};
|
||||
}
|
||||
|
||||
const yearCondition = () => (value) => {
|
||||
const yearCondition = () => value => {
|
||||
return getTransformCondition('YEAR', value);
|
||||
};
|
||||
|
||||
const yearMonthCondition = () => (value) => {
|
||||
const yearMonthCondition = () => value => {
|
||||
const m = value.match(/(\d\d\d\d)-(\d\d?)/);
|
||||
|
||||
return {
|
||||
@@ -126,7 +98,7 @@ const yearMonthCondition = () => (value) => {
|
||||
};
|
||||
};
|
||||
|
||||
const yearMonthDayCondition = () => (value) => {
|
||||
const yearMonthDayCondition = () => value => {
|
||||
const m = value.match(/(\d\d\d\d)-(\d\d?)-(\d\d?)/);
|
||||
|
||||
return {
|
||||
@@ -139,6 +111,72 @@ const yearMonthDayCondition = () => (value) => {
|
||||
};
|
||||
};
|
||||
|
||||
const createIntervalCondition = (start, end) => {
|
||||
return {
|
||||
conditionType: 'and',
|
||||
conditions: [
|
||||
{
|
||||
conditionType: 'binary',
|
||||
operator: '>=',
|
||||
left: {
|
||||
exprType: 'placeholder',
|
||||
},
|
||||
right: {
|
||||
exprType: 'value',
|
||||
value: start,
|
||||
},
|
||||
},
|
||||
{
|
||||
conditionType: 'binary',
|
||||
operator: '<=',
|
||||
left: {
|
||||
exprType: 'placeholder',
|
||||
},
|
||||
right: {
|
||||
exprType: 'value',
|
||||
value: end,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const createDateIntervalCondition = (start, end) => {
|
||||
return createIntervalCondition(start.format('YYYY-MM-DDTHH:mm:ss.SSS'), end.format('YYYY-MM-DDTHH:mm:ss.SSS'));
|
||||
};
|
||||
|
||||
const fixedMomentIntervalCondition = (intervalType, diff) => () => {
|
||||
return createDateIntervalCondition(
|
||||
moment().add(intervalType, diff).startOf(intervalType),
|
||||
moment().add(intervalType, diff).endOf(intervalType)
|
||||
);
|
||||
};
|
||||
|
||||
const yearMonthDayMinuteCondition = () => value => {
|
||||
const m = value.match(/(\d\d\d\d)-(\d\d?)-(\d\d?)\s+(\d\d?):(\d\d?)/);
|
||||
const year = m[1];
|
||||
const month = m[2];
|
||||
const day = m[3];
|
||||
const hour = m[4];
|
||||
const minute = m[5];
|
||||
const dateObject = new Date(year, month - 1, day, hour, minute);
|
||||
|
||||
return createDateIntervalCondition(moment(dateObject).startOf('minute'), moment(dateObject).endOf('minute'));
|
||||
};
|
||||
|
||||
const yearMonthDaySecondCondition = () => value => {
|
||||
const m = value.match(/(\d\d\d\d)-(\d\d?)-(\d\d?)(T|\s+)(\d\d?):(\d\d?):(\d\d?)/);
|
||||
const year = m[1];
|
||||
const month = m[2];
|
||||
const day = m[3];
|
||||
const hour = m[5];
|
||||
const minute = m[6];
|
||||
const second = m[7];
|
||||
const dateObject = new Date(year, month - 1, day, hour, minute, second);
|
||||
|
||||
return createDateIntervalCondition(moment(dateObject).startOf('second'), moment(dateObject).endOf('second'));
|
||||
};
|
||||
|
||||
const createParser = (filterType: FilterType) => {
|
||||
const langDef = {
|
||||
string1: () =>
|
||||
@@ -171,37 +209,64 @@ const createParser = (filterType: FilterType) => {
|
||||
yearNum: () => P.regexp(/\d\d\d\d/).map(yearCondition()),
|
||||
yearMonthNum: () => P.regexp(/\d\d\d\d-\d\d?/).map(yearMonthCondition()),
|
||||
yearMonthDayNum: () => P.regexp(/\d\d\d\d-\d\d?-\d\d?/).map(yearMonthDayCondition()),
|
||||
yearMonthDayMinute: () => P.regexp(/\d\d\d\d-\d\d?-\d\d?\s+\d\d?:\d\d?/).map(yearMonthDayMinuteCondition()),
|
||||
yearMonthDaySecond: () =>
|
||||
P.regexp(/\d\d\d\d-\d\d?-\d\d?(\s+|T)\d\d?:\d\d?:\d\d?/).map(yearMonthDaySecondCondition()),
|
||||
|
||||
value: (r) => P.alt(...allowedValues.map((x) => r[x])),
|
||||
valueTestEq: (r) => r.value.map(binaryCondition('=')),
|
||||
valueTestStr: (r) => r.value.map(likeCondition('like', '%#VALUE#%')),
|
||||
value: r => P.alt(...allowedValues.map(x => r[x])),
|
||||
valueTestEq: r => r.value.map(binaryCondition('=')),
|
||||
valueTestStr: r => r.value.map(likeCondition('like', '%#VALUE#%')),
|
||||
|
||||
comma: () => word(','),
|
||||
not: () => word('NOT'),
|
||||
notNull: (r) => r.not.then(r.null).map(unaryCondition('isNotNull')),
|
||||
notNull: r => r.not.then(r.null).map(unaryCondition('isNotNull')),
|
||||
null: () => word('NULL').map(unaryCondition('isNull')),
|
||||
empty: () => word('EMPTY').map(unaryCondition('isEmpty')),
|
||||
notEmpty: (r) => r.not.then(r.empty).map(unaryCondition('isNotEmpty')),
|
||||
notEmpty: r => r.not.then(r.empty).map(unaryCondition('isNotEmpty')),
|
||||
true: () => word('TRUE').map(binaryFixedValueCondition(1)),
|
||||
false: () => word('FALSE').map(binaryFixedValueCondition(0)),
|
||||
trueNum: () => word('1').map(binaryFixedValueCondition(1)),
|
||||
falseNum: () => word('0').map(binaryFixedValueCondition(0)),
|
||||
eq: (r) => word('=').then(r.value).map(binaryCondition('=')),
|
||||
ne: (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('>=')),
|
||||
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#%')),
|
||||
startsWithNot: (r) => word('!^').then(r.value).map(likeCondition('like', '#VALUE#%')).map(negateCondition),
|
||||
endsWithNot: (r) => word('!$').then(r.value).map(likeCondition('like', '%#VALUE#')).map(negateCondition),
|
||||
containsNot: (r) => word('~').then(r.value).map(likeCondition('like', '%#VALUE#%')).map(negateCondition),
|
||||
|
||||
element: (r) => P.alt(...allowedElements.map((x) => r[x])).trim(whitespace),
|
||||
factor: (r) => r.element.sepBy(whitespace).map(compoudCondition('and')),
|
||||
list: (r) => r.factor.sepBy(r.comma).map(compoudCondition('or')),
|
||||
this: () => word('THIS'),
|
||||
last: () => word('LAST'),
|
||||
next: () => word('NEXT'),
|
||||
week: () => word('WEEK'),
|
||||
month: () => word('MONTH'),
|
||||
year: () => word('YEAR'),
|
||||
|
||||
yesterday: () => word('YESTERDAY').map(fixedMomentIntervalCondition('day', -1)),
|
||||
today: () => word('TODAY').map(fixedMomentIntervalCondition('day', 0)),
|
||||
tomorrow: () => word('TOMORROW').map(fixedMomentIntervalCondition('day', 1)),
|
||||
|
||||
lastWeek: r => r.last.then(r.week).map(fixedMomentIntervalCondition('week', -1)),
|
||||
thisWeek: r => r.this.then(r.week).map(fixedMomentIntervalCondition('week', 0)),
|
||||
nextWeek: r => r.next.then(r.week).map(fixedMomentIntervalCondition('week', 1)),
|
||||
|
||||
lastMonth: r => r.last.then(r.month).map(fixedMomentIntervalCondition('month', -1)),
|
||||
thisMonth: r => r.this.then(r.month).map(fixedMomentIntervalCondition('month', 0)),
|
||||
nextMonth: r => r.next.then(r.month).map(fixedMomentIntervalCondition('month', 1)),
|
||||
|
||||
lastYear: r => r.last.then(r.year).map(fixedMomentIntervalCondition('year', -1)),
|
||||
thisYear: r => r.this.then(r.year).map(fixedMomentIntervalCondition('year', 0)),
|
||||
nextYear: r => r.next.then(r.year).map(fixedMomentIntervalCondition('year', 1)),
|
||||
|
||||
eq: r => word('=').then(r.value).map(binaryCondition('=')),
|
||||
ne: 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('>=')),
|
||||
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#%')),
|
||||
startsWithNot: r => word('!^').then(r.value).map(likeCondition('like', '#VALUE#%')).map(negateCondition),
|
||||
endsWithNot: r => word('!$').then(r.value).map(likeCondition('like', '%#VALUE#')).map(negateCondition),
|
||||
containsNot: r => word('~').then(r.value).map(likeCondition('like', '%#VALUE#%')).map(negateCondition),
|
||||
|
||||
element: r => P.alt(...allowedElements.map(x => r[x])).trim(whitespace),
|
||||
factor: r => r.element.sepBy(whitespace).map(compoudCondition('and')),
|
||||
list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')),
|
||||
};
|
||||
|
||||
const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString'];
|
||||
@@ -222,7 +287,26 @@ const createParser = (filterType: FilterType) => {
|
||||
'containsNot'
|
||||
);
|
||||
if (filterType == 'logical') allowedElements.push('true', 'false', 'trueNum', 'falseNum');
|
||||
if (filterType == 'datetime') allowedElements.push('yearMonthDayNum', 'yearMonthNum', 'yearNum');
|
||||
if (filterType == 'datetime')
|
||||
allowedElements.push(
|
||||
'yearMonthDaySecond',
|
||||
'yearMonthDayMinute',
|
||||
'yearMonthDayNum',
|
||||
'yearMonthNum',
|
||||
'yearNum',
|
||||
'yesterday',
|
||||
'today',
|
||||
'tomorrow',
|
||||
'lastWeek',
|
||||
'thisWeek',
|
||||
'nextWeek',
|
||||
'lastMonth',
|
||||
'thisMonth',
|
||||
'nextMonth',
|
||||
'lastYear',
|
||||
'thisYear',
|
||||
'nextYear'
|
||||
);
|
||||
|
||||
// must be last
|
||||
if (filterType == 'string') allowedElements.push('valueTestStr');
|
||||
@@ -236,9 +320,12 @@ const parsers = {
|
||||
string: createParser('string'),
|
||||
datetime: createParser('datetime'),
|
||||
logical: createParser('logical'),
|
||||
mongo: mongoParser,
|
||||
};
|
||||
|
||||
export function parseFilter(value: string, filterType: FilterType): Condition {
|
||||
// console.log('PARSING', value, 'WITH', filterType);
|
||||
const ast = parsers[filterType].list.tryParse(value);
|
||||
// console.log('AST', ast);
|
||||
return ast;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// import types from 'dbgate-types';
|
||||
|
||||
export type FilterType = 'number' | 'string' | 'datetime' | 'logical';
|
||||
export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'mongo';
|
||||
|
||||
@@ -37,7 +37,7 @@ console.log("Generated query:", sql);
|
||||
|
||||
```
|
||||
|
||||
See [TypeScript definitions](https://github.com/dbshell/dbgate/blob/master/packages/sqltree/src/types.ts) for complete list of available SQL command options.
|
||||
See [TypeScript definitions](https://github.com/dbgate/dbgate/blob/master/packages/sqltree/src/types.ts) for complete list of available SQL command options.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"version": "1.0.4",
|
||||
"version": "4.0.0",
|
||||
"name": "dbgate-sqltree",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbshell/dbgate.git"
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur",
|
||||
"author": "Jan Prochazka",
|
||||
"license": "GPL",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"sql",
|
||||
"mssql",
|
||||
@@ -29,10 +29,10 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^13.7.0",
|
||||
"dbgate-types": "^1.0.0",
|
||||
"dbgate-types": "^4.0.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.15"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
|
||||
if (cmd.columns) {
|
||||
if (cmd.selectAll) dmp.put('&n,');
|
||||
dmp.put('&>&n');
|
||||
dmp.putCollection(',&n', cmd.columns, (fld) => {
|
||||
dmp.putCollection(',&n', cmd.columns, fld => {
|
||||
dumpSqlExpression(dmp, fld);
|
||||
if (fld.alias) dmp.put(' ^as %i', fld.alias);
|
||||
});
|
||||
@@ -33,7 +33,7 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
|
||||
}
|
||||
if (cmd.groupBy) {
|
||||
dmp.put('&n^group ^by ');
|
||||
dmp.putCollection(', ', cmd.groupBy, (expr) => dumpSqlExpression(dmp, expr));
|
||||
dmp.putCollection(', ', cmd.groupBy, expr => dumpSqlExpression(dmp, expr));
|
||||
dmp.put('&n');
|
||||
}
|
||||
if (cmd.having) {
|
||||
@@ -43,7 +43,7 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
|
||||
}
|
||||
if (cmd.orderBy) {
|
||||
dmp.put('&n^order ^by ');
|
||||
dmp.putCollection(', ', cmd.orderBy, (expr) => {
|
||||
dmp.putCollection(', ', cmd.orderBy, expr => {
|
||||
dumpSqlExpression(dmp, expr);
|
||||
dmp.put(' %k', expr.direction);
|
||||
});
|
||||
@@ -67,7 +67,7 @@ export function dumpSqlUpdate(dmp: SqlDumper, cmd: Update) {
|
||||
|
||||
dmp.put('&n^set ');
|
||||
dmp.put('&>');
|
||||
dmp.putCollection(', ', cmd.fields, (col) => {
|
||||
dmp.putCollection(', ', cmd.fields, col => {
|
||||
dmp.put('%i=', col.targetColumn);
|
||||
dumpSqlExpression(dmp, col);
|
||||
});
|
||||
@@ -95,9 +95,9 @@ export function dumpSqlInsert(dmp: SqlDumper, cmd: Insert) {
|
||||
dmp.put(
|
||||
'^insert ^into %f (%,i) ^values (',
|
||||
cmd.targetTable,
|
||||
cmd.fields.map((x) => x.targetColumn)
|
||||
cmd.fields.map(x => x.targetColumn)
|
||||
);
|
||||
dmp.putCollection(',', cmd.fields, (x) => dumpSqlExpression(dmp, x));
|
||||
dmp.putCollection(',', cmd.fields, x => dumpSqlExpression(dmp, x));
|
||||
dmp.put(')');
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
|
||||
break;
|
||||
case 'and':
|
||||
case 'or':
|
||||
dmp.putCollection(` ^${condition.conditionType} `, condition.conditions, (cond) => {
|
||||
dmp.putCollection(` ^${condition.conditionType} `, condition.conditions, cond => {
|
||||
dmp.putRaw('(');
|
||||
dumpSqlCondition(dmp, cond);
|
||||
dmp.putRaw(')');
|
||||
|
||||
@@ -31,7 +31,7 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
|
||||
case 'call':
|
||||
dmp.put('%s(', expr.func);
|
||||
if (expr.argsPrefix) dmp.put('%s ', expr.argsPrefix);
|
||||
dmp.putCollection(',', expr.args, (x) => dumpSqlExpression(dmp, x));
|
||||
dmp.putCollection(',', expr.args, x => dumpSqlExpression(dmp, x));
|
||||
dmp.put(')');
|
||||
break;
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ export function dumpSqlRelation(dmp: SqlDumper, from: Relation) {
|
||||
dumpSqlSourceDef(dmp, from);
|
||||
if (from.conditions && from.conditions.length > 0) {
|
||||
dmp.put(' ^on ');
|
||||
dmp.putCollection(' ^and ', from.conditions, (cond) => dumpSqlCondition(dmp, cond));
|
||||
dmp.putCollection(' ^and ', from.conditions, cond => dumpSqlCondition(dmp, cond));
|
||||
}
|
||||
}
|
||||
|
||||
export function dumpSqlFromDefinition(dmp: SqlDumper, from: FromDefinition) {
|
||||
dumpSqlSourceDef(dmp, from);
|
||||
dmp.put(' ');
|
||||
if (from.relations) from.relations.forEach((rel) => dumpSqlRelation(dmp, rel));
|
||||
if (from.relations) from.relations.forEach(rel => dumpSqlRelation(dmp, rel));
|
||||
}
|
||||
|
||||
@@ -48,9 +48,9 @@ export function evaluateCondition(condition: Condition, values) {
|
||||
case 'isNotEmpty':
|
||||
return !isEmpty(evaluateExpression(condition.expr, values));
|
||||
case 'and':
|
||||
return condition.conditions.every((cond) => evaluateCondition(cond, values));
|
||||
return condition.conditions.every(cond => evaluateCondition(cond, values));
|
||||
case 'or':
|
||||
return condition.conditions.some((cond) => evaluateCondition(cond, values));
|
||||
return condition.conditions.some(cond => evaluateCondition(cond, values));
|
||||
case 'like':
|
||||
return isLike(evaluateExpression(condition.left, values), evaluateExpression(condition.right, values));
|
||||
break;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"version": "1.0.7",
|
||||
"version": "4.0.0",
|
||||
"name": "dbgate-tools",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dbshell/dbgate.git"
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur",
|
||||
"author": "Jan Prochazka",
|
||||
"license": "GPL",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"sql",
|
||||
"dbgate"
|
||||
@@ -27,7 +27,7 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^13.7.0",
|
||||
"dbgate-types": "^1.0.0",
|
||||
"dbgate-types": "^4.0.0",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^25.2.1",
|
||||
"typescript": "^3.7.5"
|
||||
|
||||
@@ -3,7 +3,7 @@ import _sortBy from 'lodash/sortBy';
|
||||
import _groupBy from 'lodash/groupBy';
|
||||
import _pick from 'lodash/pick';
|
||||
|
||||
const fp_pick = (arg) => (array) => _pick(array, arg);
|
||||
const fp_pick = arg => array => _pick(array, arg);
|
||||
export class DatabaseAnalyser {
|
||||
structure: DatabaseInfo;
|
||||
modifications: DatabaseModification[];
|
||||
@@ -40,7 +40,7 @@ export class DatabaseAnalyser {
|
||||
return this._runAnalysis();
|
||||
}
|
||||
|
||||
mergeAnalyseResult(newlyAnalysed, extractObjectId) {
|
||||
mergeAnalyseResult(newlyAnalysed) {
|
||||
if (this.structure == null) {
|
||||
return {
|
||||
...DatabaseAnalyser.createEmptyStructure(),
|
||||
@@ -49,16 +49,16 @@ export class DatabaseAnalyser {
|
||||
}
|
||||
|
||||
const res = {};
|
||||
for (const field of ['tables', 'views', 'functions', 'procedures', 'triggers']) {
|
||||
for (const field of ['tables', 'collections', 'views', 'functions', 'procedures', 'triggers']) {
|
||||
const removedIds = this.modifications
|
||||
.filter((x) => x.action == 'remove' && x.objectTypeField == field)
|
||||
.map((x) => extractObjectId(x));
|
||||
.filter(x => x.action == 'remove' && x.objectTypeField == field)
|
||||
.map(x => x.objectId);
|
||||
const newArray = newlyAnalysed[field] || [];
|
||||
const addedChangedIds = newArray.map((x) => extractObjectId(x));
|
||||
const addedChangedIds = newArray.map(x => x.objectId);
|
||||
const removeAllIds = [...removedIds, ...addedChangedIds];
|
||||
res[field] = _sortBy(
|
||||
[...this.structure[field].filter((x) => !removeAllIds.includes(extractObjectId(x))), ...newArray],
|
||||
(x) => x.pureName
|
||||
[...this.structure[field].filter(x => !removeAllIds.includes(x.objectId)), ...newArray],
|
||||
x => x.pureName
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ export class DatabaseAnalyser {
|
||||
static createEmptyStructure(): DatabaseInfo {
|
||||
return {
|
||||
tables: [],
|
||||
collections: [],
|
||||
views: [],
|
||||
functions: [],
|
||||
procedures: [],
|
||||
@@ -87,7 +88,7 @@ export class DatabaseAnalyser {
|
||||
}
|
||||
|
||||
static byTableFilter(table) {
|
||||
return (x) => x.pureName == table.pureName && x.schemaName == x.schemaName;
|
||||
return x => x.pureName == table.pureName && x.schemaName == x.schemaName;
|
||||
}
|
||||
|
||||
static extractPrimaryKeys(table, pkColumns) {
|
||||
@@ -101,7 +102,7 @@ export class DatabaseAnalyser {
|
||||
}
|
||||
static extractForeignKeys(table, fkColumns) {
|
||||
const grouped = _groupBy(fkColumns.filter(DatabaseAnalyser.byTableFilter(table)), 'constraintName');
|
||||
return Object.keys(grouped).map((constraintName) => ({
|
||||
return Object.keys(grouped).map(constraintName => ({
|
||||
constraintName,
|
||||
constraintType: 'foreignKey',
|
||||
..._pick(grouped[constraintName][0], [
|
||||
|
||||
+206
-11
@@ -1,15 +1,24 @@
|
||||
import {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
EngineDriver,
|
||||
ForeignKeyInfo,
|
||||
FunctionInfo,
|
||||
NamedObjectInfo,
|
||||
PrimaryKeyInfo,
|
||||
ProcedureInfo,
|
||||
SqlDialect,
|
||||
TableInfo,
|
||||
TransformType,
|
||||
TriggerInfo,
|
||||
ViewInfo,
|
||||
IndexInfo,
|
||||
UniqueInfo,
|
||||
CheckInfo,
|
||||
} from 'dbgate-types';
|
||||
import _isString from 'lodash/isString'
|
||||
import _isNumber from 'lodash/isNumber'
|
||||
import _isDate from 'lodash/isDate'
|
||||
import _isString from 'lodash/isString';
|
||||
import _isNumber from 'lodash/isNumber';
|
||||
import _isDate from 'lodash/isDate';
|
||||
|
||||
export class SqlDumper {
|
||||
s = '';
|
||||
@@ -46,11 +55,12 @@ export class SqlDumper {
|
||||
}
|
||||
putValue(value) {
|
||||
if (value === null) this.putRaw('NULL');
|
||||
if (value === true) this.putRaw('1');
|
||||
if (value === false) this.putRaw('0');
|
||||
else if (value === true) this.putRaw('1');
|
||||
else if (value === false) this.putRaw('0');
|
||||
else if (_isString(value)) this.putStringValue(value);
|
||||
else if (_isNumber(value)) this.putRaw(value.toString());
|
||||
else if (_isDate(value)) this.putStringValue(new Date(value).toISOString());
|
||||
else this.putRaw('NULL');
|
||||
}
|
||||
putCmd(format, ...args) {
|
||||
this.put(format, ...args);
|
||||
@@ -95,7 +105,7 @@ export class SqlDumper {
|
||||
}
|
||||
putFormattedList(c, collection) {
|
||||
if (!collection) return;
|
||||
this.putCollection(', ', collection, (item) => this.putFormattedValue(c, item));
|
||||
this.putCollection(', ', collection, item => this.putFormattedValue(c, item));
|
||||
}
|
||||
put(format: string, ...args) {
|
||||
let i = 0;
|
||||
@@ -203,7 +213,7 @@ export class SqlDumper {
|
||||
|
||||
createTable(table: TableInfo) {
|
||||
this.put('^create ^table %f ( &>&n', table);
|
||||
this.putCollection(',&n', table.columns, (col) => {
|
||||
this.putCollection(',&n', table.columns, col => {
|
||||
this.put('%i ', col.columnName);
|
||||
this.columnDefinition(col);
|
||||
});
|
||||
@@ -214,11 +224,11 @@ export class SqlDumper {
|
||||
}
|
||||
this.put(
|
||||
' ^primary ^key (%,i)',
|
||||
table.primaryKey.columns.map((x) => x.columnName)
|
||||
table.primaryKey.columns.map(x => x.columnName)
|
||||
);
|
||||
}
|
||||
if (table.foreignKeys) {
|
||||
table.foreignKeys.forEach((fk) => {
|
||||
table.foreignKeys.forEach(fk => {
|
||||
this.put(',&n');
|
||||
this.createForeignKeyFore(fk);
|
||||
});
|
||||
@@ -247,9 +257,9 @@ export class SqlDumper {
|
||||
if (fk.constraintName != null) this.put('^constraint %i ', fk.constraintName);
|
||||
this.put(
|
||||
'^foreign ^key (%,i) ^references %f (%,i)',
|
||||
fk.columns.map((x) => x.columnName),
|
||||
fk.columns.map(x => x.columnName),
|
||||
{ schemaName: fk.refSchemaName, pureName: fk.refTableName },
|
||||
fk.columns.map((x) => x.refColumnName)
|
||||
fk.columns.map(x => x.refColumnName)
|
||||
);
|
||||
if (fk.deleteAction) this.put(' ^on ^delete %k', fk.deleteAction);
|
||||
if (fk.updateAction) this.put(' ^on ^update %k', fk.updateAction);
|
||||
@@ -260,4 +270,189 @@ export class SqlDumper {
|
||||
}
|
||||
|
||||
allowIdentityInsert(table: NamedObjectInfo, allow: boolean) {}
|
||||
enableConstraints(table: NamedObjectInfo, enabled: boolean) {}
|
||||
|
||||
comment(value: string) {
|
||||
if (!value) return;
|
||||
for (const line of value.split('\n')) {
|
||||
this.put(' -- %s', line.trimRight());
|
||||
}
|
||||
}
|
||||
|
||||
createView(obj: ViewInfo) {
|
||||
this.putRaw(obj.createSql);
|
||||
this.endCommand();
|
||||
}
|
||||
dropView(obj: ViewInfo, { testIfExists = false }) {
|
||||
this.putCmd('^drop ^view %f', obj);
|
||||
}
|
||||
alterView(obj: ViewInfo) {
|
||||
this.putRaw(obj.createSql.replace(/create\s+view/i, 'ALTER VIEW'));
|
||||
this.endCommand();
|
||||
}
|
||||
changeViewSchema(obj: ViewInfo, newSchema: string) {}
|
||||
renameView(obj: ViewInfo, newSchema: string) {}
|
||||
|
||||
createProcedure(obj: ProcedureInfo) {
|
||||
this.putRaw(obj.createSql);
|
||||
this.endCommand();
|
||||
}
|
||||
dropProcedure(obj: ProcedureInfo, { testIfExists = false }) {
|
||||
this.putCmd('^drop ^procedure %f', obj);
|
||||
}
|
||||
alterProcedure(obj: ProcedureInfo) {
|
||||
this.putRaw(obj.createSql.replace(/create\s+procedure/i, 'ALTER PROCEDURE'));
|
||||
this.endCommand();
|
||||
}
|
||||
changeProcedureSchema(obj: ProcedureInfo, newSchema: string) {}
|
||||
renameProcedure(obj: ProcedureInfo, newSchema: string) {}
|
||||
|
||||
createFunction(obj: FunctionInfo) {
|
||||
this.putRaw(obj.createSql);
|
||||
this.endCommand();
|
||||
}
|
||||
dropFunction(obj: FunctionInfo, { testIfExists = false }) {
|
||||
this.putCmd('^drop ^function %f', obj);
|
||||
}
|
||||
alterFunction(obj: FunctionInfo) {
|
||||
this.putRaw(obj.createSql.replace(/create\s+function/i, 'ALTER FUNCTION'));
|
||||
this.endCommand();
|
||||
}
|
||||
changeFunctionSchema(obj: FunctionInfo, newSchema: string) {}
|
||||
renameFunction(obj: FunctionInfo, newSchema: string) {}
|
||||
|
||||
createTrigger(obj: TriggerInfo) {
|
||||
this.putRaw(obj.createSql);
|
||||
this.endCommand();
|
||||
}
|
||||
dropTrigger(obj: TriggerInfo, { testIfExists = false }) {
|
||||
this.putCmd('^drop ^trigger %f', obj);
|
||||
}
|
||||
alterTrigger(obj: TriggerInfo) {
|
||||
this.putRaw(obj.createSql.replace(/create\s+trigger/i, 'ALTER TRIGGER'));
|
||||
this.endCommand();
|
||||
}
|
||||
changeTriggerSchema(obj: TriggerInfo, newSchema: string) {}
|
||||
renameTrigger(obj: TriggerInfo, newSchema: string) {}
|
||||
|
||||
dropConstraint(cnt: ConstraintInfo) {
|
||||
this.putCmd('^alter ^table %f ^drop ^constraint %i', cnt, cnt.constraintName);
|
||||
}
|
||||
dropForeignKey(fk: ForeignKeyInfo) {
|
||||
if (this.dialect.explicitDropConstraint) {
|
||||
this.putCmd('^alter ^table %f ^drop ^foreign ^key %i', fk, fk.constraintName);
|
||||
} else {
|
||||
this.dropConstraint(fk);
|
||||
}
|
||||
}
|
||||
createForeignKey(fk: ForeignKeyInfo) {
|
||||
this.put('^alter ^table %f ^add ', fk);
|
||||
this.createForeignKeyFore(fk);
|
||||
this.endCommand();
|
||||
}
|
||||
dropPrimaryKey(pk: PrimaryKeyInfo) {
|
||||
if (this.dialect.explicitDropConstraint) {
|
||||
this.putCmd('^alter ^table %f ^drop ^primary ^key', pk);
|
||||
} else {
|
||||
this.dropConstraint(pk);
|
||||
}
|
||||
}
|
||||
createPrimaryKey(pk: PrimaryKeyInfo) {
|
||||
this.putCmd(
|
||||
'^alter ^table %f ^add ^constraint %i ^primary ^key (%,i)',
|
||||
pk,
|
||||
pk.constraintName,
|
||||
pk.columns.map(x => x.columnName)
|
||||
);
|
||||
}
|
||||
|
||||
dropIndex(ix: IndexInfo) {}
|
||||
createIndex(ix: IndexInfo) {}
|
||||
|
||||
dropUnique(uq: UniqueInfo) {
|
||||
this.dropConstraint(uq);
|
||||
}
|
||||
createUniqueCore(uq: UniqueInfo) {
|
||||
this.put(
|
||||
'^constraint %i ^unique (%,i)',
|
||||
uq.constraintName,
|
||||
uq.columns.map(x => x.columnName)
|
||||
);
|
||||
}
|
||||
|
||||
createUnique(uq: UniqueInfo) {
|
||||
this.put('^alter ^table %f ^add ', uq);
|
||||
this.createUniqueCore(uq);
|
||||
this.endCommand();
|
||||
}
|
||||
|
||||
dropCheck(ch: CheckInfo) {
|
||||
this.dropConstraint(ch);
|
||||
}
|
||||
|
||||
createCheckCore(ch: CheckInfo) {
|
||||
this.put('^constraint %i ^check (%s)', ch.constraintName, ch.definition);
|
||||
}
|
||||
|
||||
createCheck(ch: CheckInfo) {
|
||||
this.put('^alter ^table %f ^add ', ch);
|
||||
this.createCheckCore(ch);
|
||||
this.endCommand();
|
||||
}
|
||||
|
||||
renameConstraint(constraint: ConstraintInfo, newName: string) {}
|
||||
|
||||
createColumn(table: TableInfo, column: ColumnInfo, constraints: ConstraintInfo[]) {
|
||||
this.put('^alter ^table %f ^add %i ', table, column.columnName);
|
||||
this.columnDefinition(column);
|
||||
this.inlineConstraints(constraints);
|
||||
this.endCommand();
|
||||
}
|
||||
|
||||
inlineConstraints(constrains: ConstraintInfo[]) {
|
||||
if (constrains == null) return;
|
||||
for (const cnt of constrains) {
|
||||
if (cnt.constraintType == 'primaryKey') {
|
||||
if (cnt.constraintName != null && !this.dialect.anonymousPrimaryKey) {
|
||||
this.put(' ^constraint %i', cnt.constraintName);
|
||||
}
|
||||
this.put(' ^primary ^key ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropColumn(column: ColumnInfo) {
|
||||
this.putCmd('^alter ^table %f ^drop ^column %i', column, column.columnName);
|
||||
}
|
||||
|
||||
renameColumn(column: ColumnInfo, newName: string) {}
|
||||
|
||||
changeColumn(oldcol: ColumnInfo, newcol: ColumnInfo, constraints: ConstraintInfo[]) {}
|
||||
|
||||
dropTable(obj: TableInfo, { testIfExists = false }) {
|
||||
this.putCmd('^drop ^table %f', obj);
|
||||
}
|
||||
|
||||
changeTableSchema(obj: TableInfo, schema: string) {}
|
||||
|
||||
renameTable(obj: TableInfo, newname: string) {}
|
||||
|
||||
beginTransaction() {
|
||||
this.putCmd('^begin ^transaction');
|
||||
}
|
||||
|
||||
commitTransaction() {
|
||||
this.putCmd('^commit');
|
||||
}
|
||||
|
||||
alterProlog() {}
|
||||
alterEpilog() {}
|
||||
|
||||
selectTableIntoNewTable(sourceName: NamedObjectInfo, targetName: NamedObjectInfo) {
|
||||
this.putCmd('^select * ^into %f ^from %f', targetName, sourceName);
|
||||
}
|
||||
|
||||
truncateTable(name: NamedObjectInfo) {
|
||||
this.putCmd('^delete ^from %f', name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
import {
|
||||
DatabaseInfo,
|
||||
EngineDriver,
|
||||
FunctionInfo,
|
||||
ProcedureInfo,
|
||||
TableInfo,
|
||||
TriggerInfo,
|
||||
ViewInfo,
|
||||
} from 'dbgate-types';
|
||||
import _ from 'lodash';
|
||||
import { SqlDumper } from './SqlDumper';
|
||||
import { extendDatabaseInfo } from './structureTools';
|
||||
|
||||
interface SqlGeneratorOptions {
|
||||
dropTables: boolean;
|
||||
checkIfTableExists: boolean;
|
||||
dropReferences: boolean;
|
||||
createTables: boolean;
|
||||
createReferences: boolean;
|
||||
createForeignKeys: boolean;
|
||||
createIndexes: boolean;
|
||||
insert: boolean;
|
||||
skipAutoincrementColumn: boolean;
|
||||
disableConstraints: boolean;
|
||||
omitNulls: boolean;
|
||||
truncate: boolean;
|
||||
|
||||
dropViews: boolean;
|
||||
checkIfViewExists: boolean;
|
||||
createViews: boolean;
|
||||
|
||||
dropProcedures: boolean;
|
||||
checkIfProcedureExists: boolean;
|
||||
createProcedures: boolean;
|
||||
|
||||
dropFunctions: boolean;
|
||||
checkIfFunctionExists: boolean;
|
||||
createFunctions: boolean;
|
||||
|
||||
dropTriggers: boolean;
|
||||
checkIfTriggerExists: boolean;
|
||||
createTriggers: boolean;
|
||||
}
|
||||
|
||||
interface SqlGeneratorObject {
|
||||
schemaName: string;
|
||||
pureName: string;
|
||||
objectTypeField: 'tables' | 'views' | 'procedures' | 'functions';
|
||||
}
|
||||
|
||||
export class SqlGenerator {
|
||||
private tables: TableInfo[];
|
||||
private views: ViewInfo[];
|
||||
private procedures: ProcedureInfo[];
|
||||
private functions: FunctionInfo[];
|
||||
private triggers: TriggerInfo[];
|
||||
public dbinfo: DatabaseInfo;
|
||||
public isTruncated = false;
|
||||
public isUnhandledException = false;
|
||||
|
||||
constructor(
|
||||
dbinfo: DatabaseInfo,
|
||||
public options: SqlGeneratorOptions,
|
||||
public objects: SqlGeneratorObject[],
|
||||
public dmp: SqlDumper,
|
||||
public driver: EngineDriver,
|
||||
public pool
|
||||
) {
|
||||
this.dbinfo = extendDatabaseInfo(dbinfo);
|
||||
this.tables = this.extract('tables');
|
||||
this.views = this.extract('views');
|
||||
this.procedures = this.extract('procedures');
|
||||
this.functions = this.extract('functions');
|
||||
this.triggers = this.extract('triggers');
|
||||
}
|
||||
|
||||
private handleException = error => {
|
||||
console.log('Unhandled error', error);
|
||||
this.isUnhandledException = true;
|
||||
};
|
||||
|
||||
async dump() {
|
||||
try {
|
||||
process.on('uncaughtException', this.handleException);
|
||||
|
||||
this.dropObjects(this.procedures, 'Procedure');
|
||||
if (this.checkDumper()) return;
|
||||
this.dropObjects(this.functions, 'Function');
|
||||
if (this.checkDumper()) return;
|
||||
this.dropObjects(this.views, 'View');
|
||||
if (this.checkDumper()) return;
|
||||
this.dropObjects(this.triggers, 'Trigger');
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
this.dropTables();
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
this.createTables();
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
this.truncateTables();
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
await this.insertData();
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
this.createForeignKeys();
|
||||
if (this.checkDumper()) return;
|
||||
|
||||
this.createObjects(this.procedures, 'Procedure');
|
||||
if (this.checkDumper()) return;
|
||||
this.createObjects(this.functions, 'Function');
|
||||
if (this.checkDumper()) return;
|
||||
this.createObjects(this.views, 'View');
|
||||
if (this.checkDumper()) return;
|
||||
this.createObjects(this.triggers, 'Trigger');
|
||||
if (this.checkDumper()) return;
|
||||
} finally {
|
||||
process.off('uncaughtException', this.handleException);
|
||||
}
|
||||
}
|
||||
|
||||
createForeignKeys() {
|
||||
const fks = [];
|
||||
if (this.options.createForeignKeys) fks.push(..._.flatten(this.tables.map(x => x.foreignKeys || [])));
|
||||
if (this.options.createReferences) fks.push(..._.flatten(this.tables.map(x => x.dependencies || [])));
|
||||
for (const fk of _.uniqBy(fks, 'constraintName')) {
|
||||
this.dmp.createForeignKey(fk);
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
}
|
||||
|
||||
truncateTables() {
|
||||
if (this.options.truncate) {
|
||||
for (const table of this.tables) {
|
||||
this.dmp.truncateTable(table);
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createTables() {
|
||||
if (this.options.createTables) {
|
||||
for (const table of this.tables) {
|
||||
this.dmp.createTable({
|
||||
...table,
|
||||
foreignKeys: [],
|
||||
dependencies: [],
|
||||
indexes: [],
|
||||
});
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
}
|
||||
if (this.options.createIndexes) {
|
||||
for (const index of _.flatten(this.tables.map(x => x.indexes || []))) {
|
||||
this.dmp.createIndex(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async insertData() {
|
||||
if (!this.options.insert) return;
|
||||
|
||||
this.enableConstraints(false);
|
||||
|
||||
for (const table of this.tables) {
|
||||
await this.insertTableData(table);
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
|
||||
this.enableConstraints(true);
|
||||
}
|
||||
|
||||
checkDumper() {
|
||||
if (this.dmp.s.length > 4000000) {
|
||||
if (!this.isTruncated) {
|
||||
this.dmp.putRaw('\n');
|
||||
this.dmp.comment(' *************** SQL is truncated ******************');
|
||||
this.dmp.putRaw('\n');
|
||||
}
|
||||
this.isTruncated = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
dropObjects(list, name) {
|
||||
if (this.options[`drop${name}s`]) {
|
||||
for (const item of list) {
|
||||
this.dmp[`drop${name}`](item, { testIfExists: this.options[`checkIf${name}Exists`] });
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createObjects(list, name) {
|
||||
if (this.options[`create${name}s`]) {
|
||||
for (const item of list) {
|
||||
this.dmp[`create${name}`](item);
|
||||
if (this.checkDumper()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropTables() {
|
||||
if (this.options.dropReferences) {
|
||||
for (const fk of _.flatten(this.tables.map(x => x.dependencies || []))) {
|
||||
this.dmp.dropForeignKey(fk);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.dropTables) {
|
||||
for (const table of this.tables) {
|
||||
this.dmp.dropTable(table, { testIfExists: this.options.checkIfTableExists });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async insertTableData(table: TableInfo) {
|
||||
const dmpLocal = this.driver.createDumper();
|
||||
dmpLocal.put('^select * ^from %f', table);
|
||||
|
||||
const autoinc = table.columns.find(x => x.autoIncrement);
|
||||
if (autoinc && !this.options.skipAutoincrementColumn) {
|
||||
this.dmp.allowIdentityInsert(table, true);
|
||||
}
|
||||
|
||||
const readable = await this.driver.readQuery(this.pool, dmpLocal.s, table);
|
||||
await this.processReadable(table, readable);
|
||||
|
||||
if (autoinc && !this.options.skipAutoincrementColumn) {
|
||||
this.dmp.allowIdentityInsert(table, false);
|
||||
}
|
||||
}
|
||||
|
||||
processReadable(table: TableInfo, readable) {
|
||||
const columnsFiltered = this.options.skipAutoincrementColumn
|
||||
? table.columns.filter(x => !x.autoIncrement)
|
||||
: table.columns;
|
||||
const columnNames = columnsFiltered.map(x => x.columnName);
|
||||
let isClosed = false;
|
||||
let isHeaderRead = false;
|
||||
|
||||
return new Promise(resolve => {
|
||||
readable.on('data', chunk => {
|
||||
if (isClosed) return;
|
||||
if (!isHeaderRead) {
|
||||
isHeaderRead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.checkDumper()) {
|
||||
isClosed = true;
|
||||
resolve(undefined);
|
||||
readable.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const columnNamesCopy = this.options.omitNulls ? columnNames.filter(col => chunk[col] != null) : columnNames;
|
||||
this.dmp.put(
|
||||
'^insert ^into %f (%,i) ^values (%,v);&n',
|
||||
table,
|
||||
columnNamesCopy,
|
||||
columnNamesCopy.map(col => chunk[col])
|
||||
);
|
||||
});
|
||||
readable.on('end', () => {
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extract(objectTypeField) {
|
||||
return this.dbinfo[objectTypeField].filter(x =>
|
||||
this.objects.find(
|
||||
y => x.pureName == y.pureName && x.schemaName == y.schemaName && y.objectTypeField == objectTypeField
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
enableConstraints(enabled) {
|
||||
if (this.options.disableConstraints) {
|
||||
if (this.driver.dialect.enableConstraintsPerTable) {
|
||||
for (const table of this.tables) {
|
||||
this.dmp.enableConstraints(table, enabled);
|
||||
}
|
||||
} else {
|
||||
this.dmp.enableConstraints(null, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ export function createBulkInsertStreamBase(driver, stream, pool, name, options):
|
||||
writable.structure = null;
|
||||
writable.columnNames = null;
|
||||
|
||||
writable.addRow = async (row) => {
|
||||
writable.addRow = async row => {
|
||||
if (writable.structure) {
|
||||
writable.buffer.push(row);
|
||||
} else {
|
||||
@@ -44,8 +44,8 @@ export function createBulkInsertStreamBase(driver, stream, pool, name, options):
|
||||
}
|
||||
|
||||
writable.columnNames = _intersection(
|
||||
structure.columns.map((x) => x.columnName),
|
||||
writable.structure.columns.map((x) => x.columnName)
|
||||
structure.columns.map(x => x.columnName),
|
||||
writable.structure.columns.map(x => x.columnName)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -56,19 +56,20 @@ export function createBulkInsertStreamBase(driver, stream, pool, name, options):
|
||||
const dmp = driver.createDumper();
|
||||
|
||||
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
|
||||
dmp.putCollection(',', writable.columnNames, (col) => dmp.putRaw(driver.dialect.quoteIdentifier(col)));
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col)));
|
||||
dmp.putRaw(')\n VALUES\n');
|
||||
|
||||
let wasRow = false;
|
||||
for (const row of rows) {
|
||||
if (wasRow) dmp.putRaw(',\n');
|
||||
dmp.putRaw('(');
|
||||
dmp.putCollection(',', writable.columnNames, (col) => dmp.putValue(row[col]));
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col]));
|
||||
dmp.putRaw(')');
|
||||
wasRow = true;
|
||||
}
|
||||
dmp.putRaw(';');
|
||||
// require('fs').writeFileSync('/home/jena/test.sql', dmp.s);
|
||||
// console.log(dmp.s);
|
||||
await driver.query(pool, dmp.s);
|
||||
};
|
||||
|
||||
@@ -84,7 +85,7 @@ export function createBulkInsertStreamBase(driver, stream, pool, name, options):
|
||||
callback();
|
||||
};
|
||||
|
||||
writable._final = async (callback) => {
|
||||
writable._final = async callback => {
|
||||
await writable.send();
|
||||
callback();
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user