Compare commits

...

1143 Commits

Author SHA1 Message Date
Jan Prochazka
95f580d51c v5.3.5-beta.6 2024-08-21 10:16:39 +02:00
Jan Prochazka
2b9fa9a70f oracle fix + package optimalization 2024-08-21 09:54:10 +02:00
Jan Prochazka
1cbeeac7cd nosql db WIP 2024-08-20 17:01:17 +02:00
Jan Prochazka
d131287ca0 Cosmos name 2024-08-20 16:45:44 +02:00
Jan Prochazka
9f553ef52a fixed ordering 2024-08-20 14:24:15 +02:00
Jan Prochazka
781d6f1585 fix nosql ordering 2024-08-20 14:21:23 +02:00
Jan Prochazka
76c8f8ef62 mongo sorts - moved to plugin 2024-08-20 14:00:58 +02:00
Jan Prochazka
49e338bbbc conditionType expression 2024-08-20 13:30:53 +02:00
Jan Prochazka
968e69c7f2 DBGATE_TOOLS => DBGATE_PACKAGES 2024-08-20 12:18:55 +02:00
Jan Prochazka
8a69e94d79 Merge branch 'mongo-condition-refactor' 2024-08-20 10:30:25 +02:00
Jan Prochazka
80a4d3f238 perspectives - remove mongo hardcodes 2024-08-20 10:28:02 +02:00
Jan Prochazka
30e3bc6eeb mongo driver - collection export scripts 2024-08-20 09:15:10 +02:00
Jan Prochazka
9bc654cd38 mongo refactor WIP 2024-08-19 17:07:21 +02:00
Jan Prochazka
b9ad63c926 simplified filter compiling - merged into one compiler 2024-08-19 16:50:19 +02:00
Jan Prochazka
4bdcf219f2 datetime filters added to standard filters 2024-08-19 16:47:54 +02:00
Jan Prochazka
303bd659ad mongo filtering via sql tree 2024-08-19 16:25:16 +02:00
Jan Prochazka
9fedfcbb0e mongo condition refactor 2024-08-19 15:31:54 +02:00
Jan Prochazka
8cffeaa767 dynamic filter 2024-08-19 14:03:07 +02:00
Jan Prochazka
9e28f6f3aa Merge branch 'filter-refactor' 2024-08-19 13:20:15 +02:00
Jan Prochazka
19377bbeed commented logs 2024-08-19 13:19:33 +02:00
Jan Prochazka
12d60c7ed9 fixes 2024-08-19 13:16:13 +02:00
Jan Prochazka
64e770f51e filter behaviour refactor 2024-08-19 12:57:50 +02:00
Jan Prochazka
17cf9d5007 filter behaviour WIP 2024-08-19 12:51:38 +02:00
Jan Prochazka
c3609e8c7b filter behaviour WIP 2024-08-19 12:49:26 +02:00
Jan Prochazka
2a48e0c4a0 structured filter type => filterBehaviour 2024-08-19 10:26:18 +02:00
Jan Prochazka
d0fa565704 refactor WIP 2024-08-19 10:23:02 +02:00
Jan Prochazka
b30286cd11 removed obsolete code 2024-08-19 09:30:41 +02:00
Jan Prochazka
4b5c136589 set filter modal refactor 2024-08-19 09:27:51 +02:00
Jan Prochazka
84cd9d53b5 data filter control 2024-08-19 09:00:44 +02:00
Jan Prochazka
2ef4b534e3 filter refactor WIP 2024-08-16 16:51:04 +02:00
Jan Prochazka
b7c7e41375 filter type refactor WIP 2024-08-16 16:46:55 +02:00
Jan Prochazka
c0d664d399 generic drop collection 2024-08-16 12:52:37 +02:00
Jan Prochazka
a89cb607b4 create collection - generic operation 2024-08-16 12:40:44 +02:00
Jan Prochazka
ecde2da2af fixes 2024-08-15 14:52:38 +02:00
Jan Prochazka
7193a4d26c fix 2024-08-14 17:16:09 +02:00
Jan Prochazka
38d8a471b3 fix 2024-08-14 16:33:30 +02:00
Jan Prochazka
a9f9085daa token checking 2024-08-14 15:34:08 +02:00
Jan Prochazka
83ce5710ae electron auth proxy WIP 2024-08-14 14:23:00 +02:00
Jan Prochazka
ddf385caac Merge branch 'license-refactor' 2024-08-14 13:12:54 +02:00
Jan Prochazka
c582902902 save license key 2024-08-14 13:12:06 +02:00
Jan Prochazka
e9cd1906bc licence key 2024-08-14 12:34:24 +02:00
Jan Prochazka
2706297142 wip 2024-08-13 16:37:05 +02:00
Jan Prochazka
75465bf415 license refactor WIP 2024-08-13 16:29:07 +02:00
Jan Prochazka
42a79b2557 fix 2024-08-13 13:53:14 +02:00
Jan Prochazka
838bc34a4f azure auth moved to auth proxy 2024-08-13 13:24:34 +02:00
Jan Prochazka
63cdb4e507 UX 2024-08-12 10:55:08 +02:00
Jan Prochazka
ff3c39ccad fix 2024-08-09 18:13:06 +02:00
Jan Prochazka
49597b4b01 indicator of changed rows in save button 2024-08-09 17:36:22 +02:00
Jan Prochazka
a3b7490849 allowedDatabases fix 2024-08-09 08:30:15 +02:00
Jan Prochazka
45a1c58dc5 allowed databases - env variable 2024-08-08 14:17:05 +02:00
Jan Prochazka
61b9fd9210 allowed databases config 2024-08-08 14:14:13 +02:00
SPRINX0\prochazka
7c8156fbb9 v5.3.5-beta.3 2024-08-08 12:30:48 +02:00
SPRINX0\prochazka
7a0635234a Merge branch 'develop' 2024-08-08 12:30:30 +02:00
Jan Prochazka
7e5364d400 msentra auth 2024-08-08 11:45:21 +02:00
Jan Prochazka
cfa08286de authProvider.redirect is async 2024-08-08 10:51:12 +02:00
Jan Prochazka
9132bfb656 azure auth - moved from plugin into API 2024-08-08 10:30:39 +02:00
Jan Prochazka
a9352f2a93 config error detection 2024-08-08 09:46:42 +02:00
Jan Prochazka
47729d8cc3 auto login for single provider 2024-08-08 09:16:50 +02:00
Jan Prochazka
e537b43563 multiauth 2024-08-07 17:02:19 +02:00
Jan Prochazka
5f14da3844 multiauth refactor 2024-08-07 16:28:24 +02:00
Jan Prochazka
e179b0f20b logout fix 2024-08-07 15:13:47 +02:00
Jan Prochazka
35532b718a multiauth WIP 2024-08-07 14:47:33 +02:00
Jan Prochazka
42c71c1204 multiauth WIP 2024-08-07 13:58:44 +02:00
Jan Prochazka
591945dc93 css 2024-08-07 12:26:28 +02:00
Jan Prochazka
ecfaa7198b multiauth 2024-08-07 12:11:03 +02:00
Jan Prochazka
27e714111b v5.3.5-beta.2 2024-08-06 15:04:28 +02:00
Jan Prochazka
c086eaa510 Merge branch 'develop' 2024-08-06 15:01:16 +02:00
Jan Prochazka
a7444a1475 error page handling fixes 2024-08-06 14:59:09 +02:00
Jan Prochazka
399298d3bb don't open new connection on startup, when new.connection is not enabled 2024-08-06 12:53:30 +02:00
Jan Prochazka
196c0b8a3e auth db login workflow 2024-08-06 12:45:28 +02:00
Jan Prochazka
5d6d827044 single connection support 2024-08-06 10:58:18 +02:00
Jan Prochazka
2440d6b75f Merge branch 'master' into develop 2024-08-06 10:24:42 +02:00
Jan Prochazka
623456b0a7 v5.3.5-beta.1 2024-08-06 08:55:16 +02:00
Jan Prochazka
9bfb37ab94 Revert "v5.3.5-beta.1"
This reverts commit 630d909b73.
2024-08-06 08:54:49 +02:00
Jan Prochazka
630d909b73 v5.3.5-beta.1 2024-08-06 08:52:54 +02:00
Jan Prochazka
33552e30b7 oracle - reporting error line numbers 2024-08-06 08:47:10 +02:00
Jan Prochazka
a64504ba02 single conn 2024-08-05 17:15:30 +02:00
Jan Prochazka
04b195f4c6 dblogin auth 2024-08-05 17:00:29 +02:00
Jan Prochazka
17fd9035ee azure auth works 2024-08-05 14:12:24 +02:00
Jan Prochazka
f867cc5a1e volatile connection map 2024-08-05 14:03:48 +02:00
Jan Prochazka
97aa563fe7 azure auth 2024-08-05 12:56:43 +02:00
Jan Prochazka
fb2e261a08 azure auth 2024-08-05 11:56:49 +02:00
Jan Prochazka
aad4df419c changelog 2024-08-05 09:41:36 +02:00
Jan Prochazka
de60f1b335 Merge branch 'master' into develop 2024-08-05 09:32:44 +02:00
Jan Prochazka
c0c06a2099 v5.3.4 2024-08-05 09:29:59 +02:00
Jan Prochazka
bcfb54b7c7 v5.3.4-beta.1 2024-08-05 09:12:45 +02:00
Jan Prochazka
8b56ebfb39 fixed toolstrip bars for editors #861 2024-08-05 09:09:58 +02:00
Jan Prochazka
1128fe6c8f fixed app startup #862 2024-08-05 08:40:12 +02:00
Jan Prochazka
db5f5a9153 fixed app startup #862 2024-08-05 08:39:48 +02:00
Jan Prochazka
25fb3b71ca volatile connection 2024-08-02 16:39:07 +02:00
Jan Prochazka
a6822dd293 azure auth - access token obtained 2024-08-02 16:09:59 +02:00
Jan Prochazka
112513a569 azure aith wip 2024-08-02 15:12:38 +02:00
Jan Prochazka
fc448ed578 azure auth WIP 2024-08-02 14:32:28 +02:00
Jan Prochazka
f777530b1c database login support 2024-08-02 12:25:19 +02:00
Jan Prochazka
7fcebedcdd getConnectionLabel refactor 2024-08-02 11:49:45 +02:00
Jan Prochazka
cf39fd59f9 fixed notifying volatile connections (used for askUser password scenarios) 2024-08-02 10:01:02 +02:00
Jan Prochazka
6beecd157f fix 2024-08-01 12:56:12 +02:00
Jan Prochazka
57b26a2729 Merge branch 'master' into develop 2024-08-01 08:40:41 +02:00
Jan Prochazka
6ee8ca5f86 changelog 2024-08-01 08:36:22 +02:00
Jan Prochazka
1adf1da0eb v5.3.3 2024-08-01 08:27:17 +02:00
Jan Prochazka
d537a75d83 license checker 2024-07-31 16:13:59 +02:00
Jan Prochazka
2e847eee9b license checking 2024-07-31 15:36:55 +02:00
Jan Prochazka
07cb4defe6 oracledb docker install 2024-07-31 14:07:49 +02:00
Jan Prochazka
74a597164e fix 2024-07-31 13:44:53 +02:00
Jan Prochazka
f7f4a0ed3f fix 2024-07-31 13:39:48 +02:00
Jan Prochazka
dc45b1e75f oracle thick mode available for electron app 2024-07-31 12:06:02 +02:00
Jan Prochazka
5e68ce3218 oracle thick mode support #843 2024-07-31 11:20:31 +02:00
Jan Prochazka
faf6339b41 fixes 2024-07-31 09:16:46 +02:00
Jan Prochazka
33cd3b0647 oauth in storage 2024-07-30 17:30:45 +02:00
Jan Prochazka
4c5da50a04 connections per role 2024-07-30 16:26:02 +02:00
Jan Prochazka
2c805b3357 admin page fix 2024-07-30 15:31:51 +02:00
Jan Prochazka
f345c80144 fixes 2024-07-30 13:29:39 +02:00
Jan Prochazka
4346147bfc improved tabControl tabs scrolling #730 2024-07-30 13:25:33 +02:00
Jan Prochazka
b0405855aa storage permissions 2024-07-30 13:01:34 +02:00
Jan Prochazka
53ee6eacb2 inner activator 2024-07-30 10:23:14 +02:00
SPRINX0\prochazka
f39b3dd347 Merge branch 'master' into develop 2024-07-30 09:50:09 +02:00
SPRINX0\prochazka
385f8ff5fd v5.3.2-beta.3 2024-07-30 09:42:04 +02:00
Jan Prochazka
fad8e91c7e #810 hide non active tool strip containers 2024-07-30 09:36:57 +02:00
Jan Prochazka
74b0216714 context menu activator #810 2024-07-30 09:06:25 +02:00
Jan Prochazka
af3529e5e7 fix 2024-07-30 08:37:28 +02:00
Jan Prochazka
d3936ae3ec permissions WIP 2024-07-29 15:46:18 +02:00
Jan Prochazka
0afee6e3fe redirect fixes 2024-07-28 08:25:53 +02:00
Jan Prochazka
f1920549a8 admin access token 2024-07-27 12:14:01 +02:00
Jan Prochazka
b5661afdcf admin login support 2024-07-27 11:26:03 +02:00
Jan Prochazka
38a80ec695 admin login 2024-07-26 16:40:17 +02:00
Jan Prochazka
f697ba03f8 admin page support 2024-07-26 16:30:01 +02:00
Jan Prochazka
feaaa35590 auth rpovider 2024-07-26 15:03:55 +02:00
Jan Prochazka
74c04cf113 denyall provider 2024-07-26 14:21:50 +02:00
Jan Prochazka
83e15ede5c getAuthProvider 2024-07-26 14:17:33 +02:00
Jan Prochazka
6a942a5058 shouldAuthorizeApi refactor 2024-07-26 14:07:06 +02:00
Jan Prochazka
8864c3489d Merge branch 'auth-provider-refactor' into develop 2024-07-26 12:31:06 +02:00
Jan Prochazka
a4cb65b7b1 icons 2024-07-26 12:30:49 +02:00
Jan Prochazka
c3fe20b6f9 removed LOGINS variable 2024-07-26 10:12:22 +02:00
Jan Prochazka
8db941dc06 AD auth supports basic auth 2024-07-26 09:57:27 +02:00
Jan Prochazka
05329951f9 fix 2024-07-26 09:38:05 +02:00
Jan Prochazka
dd964273cd auth provider refactor 2024-07-26 09:15:22 +02:00
Jan Prochazka
c3c9ad1aed auth providert refactor WIP 2024-07-25 16:47:31 +02:00
SPRINX0\prochazka
cd8fe5d691 Merge branch 'master' into develop 2024-07-25 11:06:43 +02:00
Jan Prochazka
15d99f98f8 Fixed UUID filtering in lsqp #538 2024-07-25 10:59:43 +02:00
Jan Prochazka
be6e0f3bc8 string_agg usage fix 2024-07-25 10:47:20 +02:00
Jan Prochazka
3867b7f5ba fix: string_agg not supported in redhist 2024-07-25 10:32:16 +02:00
SPRINX0\prochazka
1b347c7e0b v5.3.2-beta.2 2024-07-25 09:35:15 +02:00
Jan Prochazka
10664b16fe try to fix linux build 2024-07-25 09:35:05 +02:00
SPRINX0\prochazka
e10e8ca161 Merge branch 'master' into develop 2024-07-24 15:43:14 +02:00
SPRINX0\prochazka
c3e05e22ad v5.3.2-beta.1 2024-07-24 15:37:51 +02:00
Jan Prochazka
97753e2b11 #839 filter out timescaledb internal objects 2024-07-24 15:31:48 +02:00
Jan Prochazka
315c0670d0 improved multi column filter #855 2024-07-24 13:52:34 +02:00
Jan Prochazka
e5fb3414fe ensure window bounds within display on startup #856 2024-07-24 13:20:44 +02:00
Jan Prochazka
227d81a01a fix 2024-07-24 12:48:02 +02:00
Jan Prochazka
bacb9510d7 fix 2024-07-24 10:16:23 +02:00
Jan Prochazka
48209509ae Merge branch 'selected-cells-refactor' into develop 2024-07-24 10:02:20 +02:00
Jan Prochazka
c2a01e4822 selected cells published refactor 2024-07-24 10:01:51 +02:00
Jan Prochazka
3e44fd823c selected cells refactor 2024-07-24 09:05:56 +02:00
Jan Prochazka
47b98041c9 Merge pull request #853 from jacobokeeffe-ow/fix/851-mongo-error-collstats
Fix 851: Loading mongo db structure fails when $collstats not supported
2024-07-11 16:00:59 +02:00
Jacob O'Keeffe
739205c192 Fix error when mongo collstats not supported 2024-07-10 16:30:19 +01:00
Jan Prochazka
8f0b44ade9 SSH info in connection refactor - do not save default values 2024-07-10 16:29:05 +02:00
Jan Prochazka
cb0a11fda9 custom grid display fix 2024-07-10 15:53:26 +02:00
Jan Prochazka
befada8b87 fix 2024-07-10 15:34:39 +02:00
Jan Prochazka
85a43c7a5b sqltree: notIn support 2024-07-10 14:36:25 +02:00
Jan Prochazka
50b64cf0c6 custyom grid display additional condition 2024-07-10 12:55:37 +02:00
Jan Prochazka
5c080568d8 changeSetInsertDocuments improved 2024-07-10 12:08:23 +02:00
Jan Prochazka
9d5c7e6df2 of not exitsts fields 2024-07-10 11:57:41 +02:00
Jan Prochazka
4864a376c6 custom grid 2024-07-09 16:09:05 +02:00
Jan Prochazka
ef77dbf768 fix 2024-07-09 14:17:27 +02:00
Jan Prochazka
7999148f3c custom data grid support 2024-07-09 13:25:39 +02:00
Jan Prochazka
ed134d787b refDeleteAction, refUpdateAction 2024-07-09 09:22:51 +02:00
Jan Prochazka
f04a3bdbd5 icons 2024-07-08 17:10:12 +02:00
Jan Prochazka
2a56b562eb changeset: support save document to SQL 2024-07-08 15:56:23 +02:00
Jan Prochazka
2199a49126 editable connection 2024-07-08 15:02:46 +02:00
Jan Prochazka
14db7b1a98 publish selectedCellsPublished 2024-07-08 12:22:16 +02:00
SPRINX0\prochazka
314b72f148 Merge branch 'master' into develop 2024-07-08 08:23:41 +02:00
Jan Prochazka
ebcad6eded Merge pull request #828 from tumit/master
feat: add 'Copy only headers' feature
2024-07-08 08:12:48 +02:00
Jan Prochazka
edad03d988 changed license, added LICENSE-OLD to plugin-oracle 2024-06-26 20:41:14 +02:00
Jan Prochazka
062c4053ca license info 2024-06-25 21:55:43 +02:00
Jan Prochazka
f20a802068 license change 2024-06-25 21:30:49 +02:00
Jan Prochazka
a612fc1649 old license 2024-06-25 21:28:12 +02:00
Jan Prochazka
bb38778853 old license 2024-06-25 21:27:22 +02:00
Jan Prochazka
409b1c84e7 Update LICENSE 2024-06-25 21:23:50 +02:00
Jan Prochazka
db8b8feb3e pro tabs 2024-06-24 16:20:44 +02:00
Jan Prochazka
6cdbfd1a89 admin menu widget 2024-06-24 14:59:36 +02:00
Jan Prochazka
49c90b9be9 icons from former develop 2024-06-24 14:52:38 +02:00
Jan Prochazka
8043869332 cherri pick file 2024-06-24 14:51:23 +02:00
Jan Prochazka
9f9c4d82da storage DB 2024-06-24 14:49:48 +02:00
Jan Prochazka
297b321bc8 convert dbmodel to json 2024-06-24 14:49:40 +02:00
Jan Prochazka
a8999855bf --version in dbmodel 2024-06-24 14:49:25 +02:00
Jan Prochazka
0c12dcaf16 storage controller 2024-06-24 14:49:18 +02:00
Jan Prochazka
954df821a5 changelog 2024-06-21 09:38:12 +02:00
Jan Prochazka
d68d9206b4 v5.3.1 2024-06-21 09:35:09 +02:00
Jan Prochazka
9d99c01018 v5.3.1-beta.3 2024-06-21 08:03:30 +02:00
Jan Prochazka
16ed91b147 fixed regression #819 2024-06-21 07:58:12 +02:00
Jan Prochazka
4d32e57947 v5.3.1-beta.2 2024-06-17 17:06:55 +02:00
Jan Prochazka
4d25fef37d fixed singledb docker connections 2024-06-17 17:06:00 +02:00
Jan Prochazka
25835ee19f v5.3.1-beta.1 2024-06-17 11:38:45 +02:00
Jan Prochazka
2cd3aac158 #816 fixed redshift DB 2024-06-17 11:38:18 +02:00
tumit
e6cdc63e61 feat: add 'Copy only headers' feature 2024-06-12 13:50:54 +07:00
Jan Prochazka
ee671297bf added version to error gist 2024-06-07 15:25:26 +02:00
Jan Prochazka
ce895f92cd Merge branch 'master' of github.com:dbgate/dbgate 2024-06-07 10:56:30 +02:00
Jan Prochazka
4e746a3055 Merge pull request #787 from michael-pattern/feat/763/per-user_permissions_when_using_oauth-follow-up
feat: per-user permissions when using oauth, follow-up
2024-06-07 10:54:45 +02:00
Jan Prochazka
5bc0af1fba changelog 2024-06-07 10:27:36 +02:00
Jan Prochazka
66a9e0d14a v5.3.0 2024-06-07 10:18:15 +02:00
Jan Prochazka
b536b56348 v5.2.10-beta.4 2024-06-03 13:09:58 +02:00
Jan Prochazka
ad6a22d2a6 #811 fixed columns in mongodb query 2024-06-03 12:43:59 +02:00
Jan Prochazka
64d9b26d79 allow specify username to redis connection 2024-06-03 11:00:13 +02:00
Jan Prochazka
284606e3d5 collection allows switch tab 2024-06-03 10:29:33 +02:00
Jan Prochazka
e8129fd499 handle error with no structure 2024-06-03 10:27:25 +02:00
Jan Prochazka
d454da325f fix: DB list for different connection 2024-06-03 10:14:17 +02:00
Jan Prochazka
5c703c786d Merge branch 'master' of github.com:dbgate/dbgate 2024-06-03 09:50:14 +02:00
Jan Prochazka
864c5bb208 switch database command 2024-06-03 09:50:08 +02:00
Jan Prochazka
504d16f189 v5.2.10-beta.3 2024-06-03 08:06:47 +02:00
Jan Prochazka
d87af2a820 oracle version getter 2024-06-03 08:06:05 +02:00
Jan Prochazka
4e6e0a79c4 oracle version fallback 2024-06-03 07:53:12 +02:00
Jan Prochazka
f4fbb28124 oracle: bulk inserter 2024-06-01 16:28:46 +02:00
Jan Prochazka
1c03a8ce9e v5.2.10-beta.2 2024-06-01 14:38:22 +02:00
Jan Prochazka
5bf2cf2784 Merge branch 'new-oracle-driver' 2024-06-01 14:38:01 +02:00
Jan Prochazka
e572cd392c removed experimental oracle status 2024-06-01 14:36:57 +02:00
Jan Prochazka
a812ff510d oracle: import data works 2024-06-01 14:35:40 +02:00
Jan Prochazka
cafe0e68c3 fixes 2024-06-01 13:08:51 +02:00
Jan Prochazka
728f3621eb fix 2024-06-01 12:37:57 +02:00
Jan Prochazka
ca0ae2084c oracle: handle statements in stream 2024-06-01 10:49:38 +02:00
Jan Prochazka
0cc7a98391 oracle: fixed commit 2024-06-01 08:36:13 +02:00
Jan Prochazka
68a40e5da6 oracle: upgraded query spliiter 2024-06-01 08:16:37 +02:00
Jan Prochazka
bbf2e2f7ed fixes 2024-06-01 08:12:18 +02:00
Jan Prochazka
1f75a818c8 oracle: code cleanup, not null detection 2024-05-31 17:03:35 +02:00
Jan Prochazka
ebdcd9ad94 oracle: detect autoincrement column 2024-05-31 16:49:43 +02:00
Jan Prochazka
2d1ac97191 oracle: fixed analysing constraints 2024-05-31 16:45:33 +02:00
Jan Prochazka
8f5b395935 oracle analyser code cleanup 2024-05-31 16:06:39 +02:00
Jan Prochazka
df60d40134 oracle - using default schema 2024-05-31 15:21:49 +02:00
Jan Prochazka
2723c41832 oracle analyser per schema 2024-05-31 14:35:35 +02:00
Jan Prochazka
a2102a51a1 use oracledb purejs client 2024-05-31 10:17:48 +02:00
Jan Prochazka
958d786dfb use .env instead of .env.local 2024-05-31 10:03:06 +02:00
Jan Prochazka
e2526082b8 v5.2.10-beta.1 2024-05-31 08:54:35 +02:00
Jan Prochazka
0d22c675b6 #803 2024-05-31 08:28:32 +02:00
Jan Prochazka
ab481121f9 changelog 2024-05-29 20:50:48 +02:00
Jan Prochazka
e9ee52ac9d v5.2.9 2024-05-29 20:45:58 +02:00
Jan Prochazka
5eecb45961 printSecrets job 2024-05-29 07:17:25 +02:00
Jan Prochazka
94e991b059 v5.2.9-beta.6 2024-05-29 07:10:33 +02:00
Jan Prochazka
373a35fe65 Revert "Merge pull request #744 from Bare7a/postgresql-user-type-enhancements"
This reverts commit fdabe1eeaa, reversing
changes made to 4429b1d618.
2024-05-29 07:09:43 +02:00
Jan Prochazka
eba5bd9c2b v5.2.9-beta.5 2024-05-27 18:06:27 +02:00
Jan Prochazka
ca3507f5d4 fixed electron logs 2024-05-27 18:04:54 +02:00
Jan Prochazka
4f5db15c20 logging 2024-05-27 14:05:44 +02:00
Jan Prochazka
22bed04d13 gist - configurable token 2024-05-27 14:01:58 +02:00
Jan Prochazka
d3737b4e08 v5.2.9-beta.4 2024-05-27 12:13:34 +02:00
Jan Prochazka
20c1315380 pg more analyser logs 2024-05-27 12:02:40 +02:00
Jan Prochazka
e9442bd633 postgreSQL - extended nalyser logs 2024-05-27 11:43:38 +02:00
Jan Prochazka
59bd699fc6 v5.2.9-beta.3 2024-05-24 14:19:41 +02:00
Jan Prochazka
cc3fd605de add gist upload secret 2024-05-24 14:18:57 +02:00
Jan Prochazka
0adf730f0b use gist secret 2024-05-24 14:13:12 +02:00
Jan Prochazka
5ab0907bd8 upload error to gist 2024-05-24 13:12:07 +02:00
Jan Prochazka
e04da15f72 fix mongo reload - using setLoadedRows instead of bind:loadedRows 2024-05-24 10:47:38 +02:00
Jan Prochazka
5fe55af3b7 feat(mongo): fixed data writes after mongo driver upgrade #798 2024-05-24 09:37:44 +02:00
Jan Prochazka
79793d1b58 socket fix 2024-05-24 09:36:04 +02:00
Jan Prochazka
d00ee890e5 fixed stream 2024-05-20 20:15:53 +02:00
Jan Prochazka
7d984d8faf feat(mjs): added crypto imports 2024-05-20 19:48:21 +02:00
Jan Prochazka
153f40f13e removed uuid dependency from api package 2024-05-20 19:15:03 +02:00
Jan Prochazka
134d8d1b1a feat(mjs): refactor 2024-05-20 19:00:11 +02:00
Jan Prochazka
2678daab4d Merge pull request #779 from Shah-Panam/Oauth-Allowed-Groups
Added OAuth Allowed Groups Option
2024-05-20 15:08:58 +02:00
Jan Prochazka
781ee15304 Merge commit '51d4bc9a75cdf28831b941f8ed73bd4644a2ca4e' 2024-05-20 14:03:18 +02:00
Jan Prochazka
51d4bc9a75 stream filtering by connection id 2024-05-20 14:02:47 +02:00
Jan Prochazka
df3313e647 don't wrap statusbar 2024-05-20 12:48:50 +02:00
michael-pattern
4214b4f613 Use LOGIN_PERMISSIONS_* to compile permissions directly instead of creating logins. Accept req.user.login in hasPermission 2024-05-17 10:59:20 -04:00
ProjectInfinity
685c0f7dbc v5.2.9-beta.2 2024-05-17 13:53:28 +02:00
ProjectInfinity
4417edf73b Change from 127.0.0.1 to localhost for SSH tunnel 2024-05-17 13:06:48 +02:00
ProjectInfinity
faf94c1a24 v5.2.9-beta.1 2024-05-17 13:02:46 +02:00
Jan Prochazka
c82a877271 #782 disable auto upgrade workaround 2024-05-17 12:15:09 +02:00
Jan Prochazka
8ba85acd3c changelog 2024-05-17 06:45:06 +02:00
Jan Prochazka
ba65704d55 v5.2.8 2024-05-17 06:40:13 +02:00
Jan Prochazka
68f77d4ed7 v5.2.8-beta.24 2024-05-15 18:57:10 +02:00
Jan Prochazka
008f6be6ac Merge branch 'master' of github.com:dbgate/dbgate 2024-05-15 18:56:57 +02:00
Jan Prochazka
7324cef87a write uncaught exception to console 2024-05-15 18:56:52 +02:00
michael-pattern
cb9921918f Make use of LOGINS and OAUTH_PERMISSIONS exclusive 2024-05-15 12:40:09 -04:00
Panam Shah
9839dc795b Added OAuth Allowed Groups Option 2024-05-14 11:40:53 +00:00
Jan Prochazka
471fcdc131 added flatpak metainfo 2024-05-13 16:28:21 +02:00
Jan Prochazka
c2abc83f99 reverted ubuntu upgrade 2024-05-13 14:47:32 +02:00
Jan Prochazka
a23bda7294 v5.2.8-beta.23 2024-05-13 14:26:55 +02:00
Jan Prochazka
a2d643305b Merge commit '9b4683ef53e815a4190ae90b8a83365235208d0e' 2024-05-13 14:26:38 +02:00
Jan Prochazka
dd36427a80 upgraded docker ubuntu base 2024-05-13 14:25:26 +02:00
Jan Prochazka
9b4683ef53 v5.2.8-beta.22 2024-05-13 09:27:57 +02:00
Jan Prochazka
a24271f045 build tar.gz for linux 2024-05-13 09:27:19 +02:00
Jan Prochazka
f74e57bec2 handle not found connection in ConnectionTab 2024-05-13 09:26:54 +02:00
Jan Prochazka
4fb6b49b86 small fix 2024-05-13 08:40:43 +02:00
Jan Prochazka
72c380cef5 v5.2.8-beta.17 2024-05-09 14:23:42 +02:00
Jan Prochazka
39cdaf88f4 Merge pull request #770 from michael-pattern/feat/763/per-user_permissions_when_using_oauth
feat: per-user permissions when using oauth
2024-05-09 14:20:12 +02:00
Jan Prochazka
52c77031c5 Merge pull request #742 from frenchtoasters/trustCertEnv
Adding env for parsing for trustServerCertificate connection value
2024-05-09 14:02:45 +02:00
Jan Prochazka
fdabe1eeaa Merge pull request #744 from Bare7a/postgresql-user-type-enhancements
Postgresql - Show proper types for Composite Types, Enums and Arrays
2024-05-09 13:21:04 +02:00
Jan Prochazka
4429b1d618 revert changes button #759 2024-05-09 13:07:31 +02:00
Jan Prochazka
5c24774170 fixed copy & paste commands 2024-05-09 13:03:05 +02:00
Jan Prochazka
792be82acd changelog 2024-05-09 12:20:34 +02:00
Jan Prochazka
6e3cd08d8a redis driver upgraded 2024-05-09 12:16:14 +02:00
michael-pattern
696d870c2f Allow password-based user login only when password is truthy 2024-05-08 17:52:50 -04:00
michael-pattern
26471517a9 Only add users to basicAuth when password is truthy 2024-05-08 17:52:05 -04:00
michael-pattern
58233a2fd5 Create login entries when the OAUTH_PERMISSIONS flag is truthy 2024-05-08 17:51:27 -04:00
Jan Prochazka
230948c4b4 changelog 2024-05-08 19:27:31 +02:00
Jan Prochazka
df593074c2 test container node version => 18 2024-05-08 19:00:28 +02:00
Jan Prochazka
474ecb1b71 Merge branch 'update-packages-2' 2024-05-08 18:55:52 +02:00
Jan Prochazka
e8e5781b59 removed better-sqlite prebuilds 2024-05-08 17:26:34 +02:00
Jan Prochazka
a042ff363e v5.2.8-beta.16 2024-05-08 10:35:59 +02:00
Jan Prochazka
63bdf817c6 upgraded webpack 2024-05-08 10:35:14 +02:00
Jan Prochazka
82eed3b86e v5.2.8-beta.15 2024-05-08 10:27:52 +02:00
Jan Prochazka
21a24f9ba2 node version => 18 2024-05-08 10:27:35 +02:00
Jan Prochazka
550354fe09 v5.2.8-beta.14 2024-05-08 10:22:45 +02:00
Jan Prochazka
e14e7efa1a fixed build error - upgraded chart.js 2024-05-08 10:22:25 +02:00
Jan Prochazka
b1cf418058 upgrade electron && db drivers 2024-05-08 10:17:45 +02:00
Jan Prochazka
bde4127b33 v5.2.8-beta.13 2024-05-08 08:33:34 +02:00
Jan Prochazka
e981cb2734 upgrade webpack 2024-05-08 08:31:10 +02:00
Jan Prochazka
6f4c0edb46 upgraded mongodb driver 2024-05-08 08:21:51 +02:00
Jan Prochazka
6591e45a6e v5.2.8-beta.12 2024-05-08 07:47:02 +02:00
Jan Prochazka
397a6b54ff Merge branch 'master' of github.com:dbgate/dbgate 2024-05-08 07:46:31 +02:00
Jan Prochazka
51555da376 fixed missing env 2024-05-08 07:46:28 +02:00
ProjectInfinity
8e6b1973c7 Merge branch 'copy-paste-improvements' 2024-04-17 17:56:51 +02:00
ProjectInfinity
5a8627c39f Add copy to column list 2024-04-17 17:44:10 +02:00
Bare7a
26a46d9037 updated tableModifications.js 2024-04-06 17:39:58 +03:00
Bare7a
b9a974ca27 updated tableModifications.js 2024-04-06 17:37:14 +03:00
Bare7a
e4ed163723 updated tableModifications.js 2024-04-06 17:33:47 +03:00
Bare7a
4d8c62f3f5 updated tableModifications.js 2024-04-06 17:31:25 +03:00
Bare7a
4b2e28483b Updated columns.js 2024-04-06 17:30:09 +03:00
Bare7a
3cd070e211 Update columns.js 2024-04-06 17:21:06 +03:00
Bare7a
1e818e7756 Postgresql - Show proper types for Composite Types, Enums and Arrays 2024-04-06 17:13:36 +03:00
Jan Prochazka
91efb7abda v5.2.8-beta.11 2024-04-05 10:23:57 +02:00
Jan Prochazka
5659311ba2 v5.2.8-beta.10 2024-04-05 10:22:54 +02:00
Jan Prochazka
232031ff5b Merge branch 'master' of github.com:dbgate/dbgate 2024-04-05 10:20:35 +02:00
Jan Prochazka
0e242321ed upgraded electron-builder-notarize 2024-04-05 10:20:31 +02:00
Tyler French
83f3391b24 Adding env for parsing for trustServerCertificate connection value 2024-04-03 15:29:19 -04:00
ProjectInfinity
715c6f7f29 Make TabControl scrollable
Fixes #730
2024-03-28 03:40:01 +01:00
ProjectInfinity
0fc20f7238 Fix filter being unclickable
A commit caused the filter input to be unclickable, this fixes that.
2024-03-26 00:09:12 +01:00
ProjectInfinity
c824e32f0a Add copy name to table list 2024-03-14 00:24:53 +01:00
ProjectInfinity
10a916bce6 Add copy/paste to query tab and database list 2024-03-13 23:59:02 +01:00
Jan Prochazka
1080147085 v5.2.8-beta.9 2024-02-16 08:43:22 +01:00
Jan Prochazka
f0ebe260e2 v5.2.6-beta.8 2024-02-16 08:42:53 +01:00
Jan Prochazka
9ad82caac5 Revert "Merge branch 'update-packages'"
This reverts commit c4f322bda2, reversing
changes made to f090661eb9.
2024-02-12 13:08:30 +01:00
Jan Prochazka
92d13dda31 v5.2.8-beta.7 2024-02-12 12:59:37 +01:00
Jan Prochazka
c4f322bda2 Merge branch 'update-packages' 2024-02-12 12:59:23 +01:00
Jan Prochazka
504bbeac52 upgraded mongodb driver 2024-02-12 12:55:20 +01:00
Infinity
f090661eb9 Merge pull request #704 from KKishikawa/fix/unicode-query-file-enc-error
fix(app): Encoding error when opening Unicode query files
2024-02-01 22:48:11 +01:00
Kenta Kishikawa
a526797013 fix: Encoding error when opening Unicode query files 2024-01-31 00:50:12 +09:00
Infinity
beb1a00874 Merge pull request #697 from KKishikawa/fix/restore-opend-tabs-correctly
fix: localStorageGabageCollector not working
2024-01-26 00:45:45 +01:00
Kenta Kishikawa
2ddf10dfda fix: localStorageGabageCollector not working 2024-01-25 22:04:15 +09:00
Luboš Nguyen
cdde770810 enum + set for mysql (#693)
* enum + set for mysql

* enum + set for mysql | dropdown

* enum for mysql | removed empty option
2024-01-23 10:05:49 +01:00
Infinity
f2f8b9ef7e Merge pull request #690 from KKishikawa/fix/correctly-select-save-folder-dump
fix(app): Correctly select the save folder for dump
2024-01-20 19:15:55 +01:00
Infinity
1e1c26a16f Update Dockerfile - Match Node version 2024-01-19 01:53:43 +01:00
Kenta Kishikawa
4b3897c7f0 fix(app): Correctly select the save folder for dump 2024-01-15 00:08:35 +09:00
Infinity
3e8dabc1e4 Merge pull request #685 from mohamedelhefni/add-vim-support
feat(web): add support for vim keybindings
2024-01-08 20:08:27 +01:00
Mohamed Elhefni
81eda4d0d3 chore(web): move keybind mode setting to Genral section in settings modal 2024-01-08 12:09:15 +02:00
Mohamed Elhefni
090329593e feat(web): add support for vim keybindings 2024-01-06 14:06:40 +02:00
Infinity
aa66367f86 Merge pull request #677 from KKishikawa/fix-download-with-auth-header
fix: download with auth header
2024-01-04 23:46:18 +01:00
ProjectInfinity
f7e43d6608 v5.2.8-beta.4 2023-12-24 15:21:50 +01:00
ProjectInfinity
c22bb6905c Use webpack5 for mysql plugin 2023-12-24 15:17:06 +01:00
ProjectInfinity
9a1a0dd0db Update workflows with node16 lts 2023-12-24 05:35:40 +01:00
ProjectInfinity
695c7c2a74 Increase mysql2 version 2023-12-24 05:28:36 +01:00
Infinity
edf6819ece Merge pull request #674 from KKishikawa/fix-mac-select-all-shotcut
fix: Mac - shortcut for select all
2023-12-24 03:58:26 +01:00
Kenta Kishikawa
9f62a15eeb fix: download with auth header 2023-12-13 01:39:31 +09:00
Infinity
46df729195 Merge pull request #646 from enwawerueli/devel
fix(web): fix typo in column filter modal
2023-12-11 21:03:09 +01:00
Kenta Kishikawa
f0fc50097b fix: Mac - shortcut for select all 2023-12-07 01:07:58 +09:00
ProjectInfinity
aa7d91f2c5 Fix inconsistencies in max/unmaximize window buttons
Also adds maximize action to command palette
2023-12-05 23:45:51 +01:00
Infinity
843675a056 Merge pull request #671 from KKishikawa/save-menu-are-not-working
fix(web): file menu save and save as not working
2023-12-05 15:38:06 +01:00
Kenta Kishikawa
129c3ed217 fix(web): file menu save and save as not working 2023-12-04 14:17:46 +00:00
Infinity
79d1b7893d Merge pull request #668 from KKishikawa/fix-import-export-query-editor-overlaps
fix: query editor on import/export screen overlaps with selector
2023-11-28 21:44:27 +01:00
Kenta Kishikawa
690940d070 fix: query editor on import/export screen overlaps with selector 2023-11-27 10:05:10 +00:00
ProjectInfinity
89132ac47a Docker: Match GH runner
Experiment to see if this solves potential glibc issues.
2023-11-09 02:20:09 +01:00
ProjectInfinity
267fe7ca1f v5.2.8-beta.1 2023-11-09 01:23:37 +01:00
ProjectInfinity
e4d18bfc43 Update Docker image to 14-bullseye
Debian Buster is so old that dependencies are starting to no longer support it.
2023-11-09 01:14:16 +01:00
enwawerueli
8bda4a7d2e fix(web): fix typo in column filter modal 2023-10-25 01:08:58 +03:00
Jan Prochazka
aea2c64703 changeog 2023-10-06 14:12:48 +02:00
Jan Prochazka
80a18ec724 v5.2.7 2023-10-06 14:06:21 +02:00
Jan Prochazka
a28aad9544 v5.2.7-beta.2 2023-09-30 12:37:57 +02:00
Jan Prochazka
ce09fcb7fd try to fix e_channel_closed error 2023-09-30 12:29:28 +02:00
Jan Prochazka
731c4c046c fixed No mapping #575 2023-09-30 11:43:50 +02:00
Jan Prochazka
a98d5d29ca fixed Data import from files #633 2023-09-30 10:28:54 +02:00
Jan Prochazka
c75ae033ba v5.2.7-beta.1 2023-09-28 11:46:13 +02:00
Jan Prochazka
067d91bf8c v5.2.7-alpha.1 2023-09-28 11:22:23 +02:00
Jan Prochazka
3a24bcebf8 Merge branch 'master' of github.com:dbgate/dbgate 2023-09-28 11:21:01 +02:00
Jan Prochazka
d6104c8375 disabled shell scripting for NPM distribution by default 2023-09-28 11:20:58 +02:00
Jan Prochazka
1c5a90226e Merge pull request #627 from peteyus/issue/626/add-headers-on-file-upload
add API headers on upload call
2023-09-28 09:33:27 +02:00
Jan Prochazka
dccfcfe8db readme 2023-09-28 09:28:15 +02:00
Jan Prochazka
069ccf814e Merge pull request #625 from bluelakee02/bluelake/fix-missing-links-readme
Remove missing links to jenasoft
2023-09-28 09:24:44 +02:00
Jan Prochazka
032f94e56a Merge pull request #596 from vasekch/patch-1
Pass signals in entrypoint.sh
2023-09-28 08:33:53 +02:00
Jan Prochazka
f95beaefff Merge pull request #592 from kikyous/master
fix body overflow when context menu height great than viewport
2023-09-28 08:32:08 +02:00
Peter Jorgensen
46afb1f1df add API headers on upload call 2023-09-18 21:56:42 -06:00
bluelakee02
c40878f1e2 Remove missing links to jenasoft 2023-09-17 16:45:57 +02:00
Vašek Chalupníček
3017fd4ed4 Update entrypoint.sh
Replace entrypoint.sh process with node process in final command to allow passing signals to the node process (e.g. SIGTERM upon stopping the container)
2023-07-18 13:08:39 +02:00
chen
2464935fa5 update style 2023-07-12 13:18:32 +08:00
Jan Prochazka
fadd5b138c readme 2023-06-02 13:11:31 +02:00
Jan Prochazka
f1212ec956 v5.2.6 2023-06-02 13:07:29 +02:00
Jan Prochazka
409278eca4 mongodb: encode username and password #560 2023-05-27 08:41:54 +02:00
Jan Prochazka
914d8dfe8a v5.2.6-beta.9 2023-05-27 08:07:23 +02:00
Jan Prochazka
914909f5de #561 used asarUnpack option 2023-05-27 08:07:11 +02:00
Jan Prochazka
a403509ee7 v5.2.6-beta.8 2023-05-26 19:35:17 +02:00
Jan Prochazka
866ce9aea7 try to fix mac build 2023-05-26 19:35:02 +02:00
Jan Prochazka
06a2a4e57d v5.2.6-beta.7 2023-05-26 18:18:47 +02:00
Jan Prochazka
4cb8f7152d changed macos runner 2023-05-26 18:18:40 +02:00
Jan Prochazka
773e446fd7 v5.2.6-beta.6 2023-05-26 17:48:56 +02:00
Jan Prochazka
b65d211def postpone creation of native modules #561 2023-05-26 17:48:22 +02:00
Jan Prochazka
171f97ee0c Revert "upgrade msnodesqlv8 - try to fix #561"
This reverts commit 756cf8a099.
2023-05-26 17:35:13 +02:00
Jan Prochazka
424bd3a036 v5.2.6-beta.2 2023-05-26 14:43:22 +02:00
Jan Prochazka
756cf8a099 upgrade msnodesqlv8 - try to fix #561 2023-05-26 14:36:54 +02:00
Jan Prochazka
d109464fdd v5.2.6-beta.1 2023-05-21 09:48:41 +02:00
Jan Prochazka
05bb40b780 Merge pull request #556 from Flusinerd/master
fix(plugins/mssql): fix typo in datetimeoffset dataType
2023-05-19 17:23:16 +02:00
Jan Prochazka
a830fadc7a Merge pull request #537 from ProjectInfinity/fix-sql-export-timestamp
Fix - SQL export is using the wrong hour formatting
2023-05-19 17:19:00 +02:00
Jan Prochazka
e7acaf3fc7 Merge pull request #524 from ProjectInfinity/improve-diagram-view
Fixes missing toolstrip and adds up to 200% zoom to diagram view
2023-05-19 17:13:08 +02:00
Jan Krüger
4f2b3c15e2 fix(plugins/mssql): fix typo in datetimeoffset dataType
fixes #555
2023-05-09 18:13:32 +02:00
Infinity
54b1cde5c9 Fix - SQL export is using the wrong hour formatting 2023-04-11 08:10:45 +00:00
Infinity
ffe1c4c7cd Fixes missing toolstrip and adds up to 200% zoom to diagram view 2023-03-26 05:00:21 +00:00
Jan Prochazka
fee438c6d1 type 2023-03-19 07:04:04 +01:00
Jan Prochazka
06b48b1c63 changelog 2023-03-17 13:36:54 +01:00
Jan Prochazka
9abb1ed19c v5.2.5 2023-03-17 13:33:32 +01:00
Jan Prochazka
58f4370bb6 v5.2.5-beta.17 2023-03-13 20:42:39 +01:00
Jan Prochazka
86c02a76d0 reverted xlsx 2023-03-13 20:42:27 +01:00
Jan Prochazka
a2bc636396 convertor from forage 2023-03-13 20:33:51 +01:00
Jan Prochazka
00bf1e64a1 removed misleading WidgetColumnBarItrem.show attr 2023-03-13 20:18:13 +01:00
Jan Prochazka
a45782098a fixes database widget collapsing problems 2023-03-13 20:03:52 +01:00
Jan Prochazka
df4230ea1d pinned objects fixes 2023-03-13 19:50:06 +01:00
Jan Prochazka
886e0a059e v5.2.5-beta.16 2023-03-12 09:09:39 +01:00
Jan Prochazka
f83c4ef799 Merge branch 'develop' 2023-03-12 09:08:40 +01:00
Jan Prochazka
66d1b4ca49 disable collapse last widget items 2023-03-12 09:06:11 +01:00
Jan Prochazka
b2f55522a8 fixed Resizing window resets window contents #479 2023-03-12 08:45:28 +01:00
Jan Prochazka
edc3a7409a tool strip wrappable, table data commands moved from statusbar to toolstrip 2023-03-12 08:36:22 +01:00
Jan Prochazka
09e584326f better mac icon 2023-03-11 11:55:23 +01:00
Jan Prochazka
feed0cd8db postgres analyse index desc #514 2023-03-11 10:33:13 +01:00
Jan Prochazka
9d4105335f v5.2.5-beta.14 2023-03-06 19:11:59 +01:00
Jan Prochazka
c15261227b upgraded ubuntu builder version 18.04=>22.04 2023-03-06 19:11:47 +01:00
Jan Prochazka
de567bdd31 v5.2.5-beta.11 2023-03-06 18:49:58 +01:00
Jan Prochazka
6736e8d0cf allow collapse multitab group 2023-03-06 18:49:30 +01:00
Jan Prochazka
36ccba7988 fixed split margin 2023-03-06 18:49:26 +01:00
Jan Prochazka
75c5d30ad3 v5.2.5-beta.9 2023-03-05 20:28:22 +01:00
Jan Prochazka
cd10095dc0 Merge branch 'master' into develop 2023-03-05 17:20:22 +01:00
Jan Prochazka
a64e42f1c2 changelog 2023-03-05 17:19:03 +01:00
Jan Prochazka
3c3f8514da v5.2.4 2023-03-05 17:17:58 +01:00
Jan Prochazka
961d11b610 split group condition 2023-03-05 17:13:05 +01:00
Jan Prochazka
c646a83608 close functions in tab group 2023-03-05 17:11:02 +01:00
Jan Prochazka
d1bdebb4ed abilit to split whole group 2023-03-05 16:59:41 +01:00
Jan Prochazka
aa4406942f tabs saves to forage instead of storage 2023-03-05 15:43:45 +01:00
Jan Prochazka
ff044ebec8 tab drag&drop fix 2023-03-05 15:28:29 +01:00
Jan Prochazka
f5d41c89e6 show selected tab in multitab 2023-03-05 12:20:07 +01:00
Jan Prochazka
d283429f40 open new tab - corrent multiTabIndex 2023-03-05 12:14:43 +01:00
Jan Prochazka
15d005be13 drag & drop between mutlitabs 2023-03-05 11:47:13 +01:00
Jan Prochazka
f404e9956e refactor - visibleSecondary not stored, computed in component 2023-03-05 11:34:42 +01:00
Jan Prochazka
2dadd1f437 vertical split tabs #394 2023-03-05 10:43:04 +01:00
Jan Prochazka
1061d2aba2 tabs container style refactor 2023-03-04 10:50:10 +01:00
Jan Prochazka
ff36870763 tabs files refactor 2023-03-04 10:39:57 +01:00
Jan Prochazka
991176d433 v5.2.4-beta.2 2023-03-03 18:26:27 +01:00
Jan Prochazka
406e3c022c mac icon #494 2023-03-03 18:25:58 +01:00
Jan Prochazka
2688c31123 v5.2.4-alpha.1 2023-03-03 18:05:08 +01:00
Jan Prochazka
578282c419 fixed reference #508 2023-03-03 18:04:51 +01:00
Jan Prochazka
9505643a26 v5.2.3 2023-02-27 18:11:49 +01:00
Jan Prochazka
2169d1a288 changelog 2023-02-26 17:07:44 +01:00
Jan Prochazka
62ebe49ac0 v5.2.3-beta.9 2023-02-26 16:50:57 +01:00
Jan Prochazka
a2043b237f multi column condition in perspectives 2023-02-26 16:48:32 +01:00
Jan Prochazka
7c03d31b84 mutli column condition for JSL data 2023-02-26 15:44:29 +01:00
Jan Prochazka
b26be02203 multi column filter #491 2023-02-26 15:26:39 +01:00
Jan Prochazka
a251e92598 filters refactor fix 2023-02-26 15:00:54 +01:00
Jan Prochazka
1a28922a62 refactor - simplified filters component 2023-02-26 14:42:54 +01:00
Jan Prochazka
65c3ff8ec9 fix 2023-02-26 11:42:19 +01:00
Jan Prochazka
56fe578884 removed free table refs 2023-02-26 11:39:30 +01:00
Jan Prochazka
4dbb3a72d4 fixed problem with closing queries in progress 2023-02-26 11:08:20 +01:00
Jan Prochazka
5fd7982f06 marked oracle support as experimantal 2023-02-26 10:15:18 +01:00
Jan Prochazka
0ca5114b71 Merge branch 'develop' 2023-02-26 10:14:42 +01:00
Jan Prochazka
d1ae7fe6e9 oracle support marked as experimental 2023-02-26 10:08:41 +01:00
Jan Prochazka
1417f53c56 disable SSL tab for oracle 2023-02-26 10:03:25 +01:00
Jan Prochazka
7a606cf8ef oracle port config #496 2023-02-26 10:01:53 +01:00
Jan Prochazka
622773fccd optimalization of loading oracle structure 2023-02-26 09:40:12 +01:00
Jan Prochazka
64ceea3779 fiuxed dependency 2023-02-26 09:05:36 +01:00
Jan Prochazka
a588d72b26 create default archive by default 2023-02-26 08:58:23 +01:00
Jan Prochazka
7ec23ecca4 fixed modify archive for windows 2023-02-26 08:41:30 +01:00
Jan Prochazka
0c62349802 fixed error reporting problems 2023-02-25 20:25:27 +01:00
Jan Prochazka
c817bf5911 added import/export tab (not used) 2023-02-25 18:24:00 +01:00
Jan Prochazka
2d74b831c5 fixed sqlite data duplicator 2023-02-25 13:33:33 +01:00
Jan Prochazka
490efb065a fixes sqlite autoincrement column creation 2023-02-25 13:31:24 +01:00
Jan Prochazka
6ccaa05bec Merge pull request #505 from mhf-ir/master
fix: connection ssl require file path instread of file content
2023-02-25 12:26:55 +01:00
Jan Prochazka
eb04f56662 fixed TS + code tidy 2023-02-25 11:57:30 +01:00
Jan Prochazka
4e97f54bd4 archive file - save as 2023-02-25 11:43:14 +01:00
Jan Prochazka
9fe689625e simplified tab register 2023-02-25 11:36:16 +01:00
Jan Prochazka
fa24d47c03 fixed tab component 2023-02-25 11:34:55 +01:00
Jan Prochazka
1c73920dd5 save jsl data 2023-02-25 11:34:19 +01:00
Jan Prochazka
a77492440e removed free table (data sheet) concept 2023-02-25 09:51:08 +01:00
Jan Prochazka
7c4a47c4c6 running row macros 2023-02-24 19:04:22 +01:00
Jan Prochazka
a519c78301 quick export - current archive 2023-02-24 17:22:11 +01:00
Jan Prochazka
d024b6f25c run macro on jsl data 2023-02-24 16:48:37 +01:00
Muhammad Hussein Fattahizadeh
0c6e113e3e fix: connection ssl require file path instread of file content 2023-02-22 18:53:41 +03:30
Jan Prochazka
6ff4acc50d removed marking archive as data sheet 2023-02-21 07:37:37 +01:00
Jan Prochazka
fabf333664 v5.2.3-beta.8 2023-02-19 19:24:00 +01:00
Jan Prochazka
29eef5619d dynamic structure switch 2023-02-19 19:23:51 +01:00
Jan Prochazka
eb098bb33a upgraded xlsx package 2023-02-17 14:13:25 +01:00
Jan Prochazka
36c792f44e excel import fix 2023-02-17 13:57:30 +01:00
Jan Prochazka
c7aaf06506 v5.2.3-beta.7 2023-02-17 12:15:19 +01:00
Jan Prochazka
7b6a1543de duplicator UX 2023-02-17 12:14:58 +01:00
Jan Prochazka
67e287cfdf added links from duplicator 2023-02-17 10:41:01 +01:00
Jan Prochazka
7802cde14d duplicator fixes 2023-02-17 10:26:44 +01:00
Jan Prochazka
6b783027e5 data duplicator fix 2023-02-17 10:00:21 +01:00
Jan Prochazka
1ab58a491a data duplicator test 2023-02-17 09:27:16 +01:00
Jan Prochazka
b6c5f26eb4 data duplicator test 2023-02-17 09:15:13 +01:00
Jan Prochazka
6a0feb235a fixed compilation error 2023-02-17 08:46:31 +01:00
Jan Prochazka
1365f2b47c duplicator options 2023-02-16 18:27:05 +01:00
Jan Prochazka
8109dd862e change theme fix 2023-02-16 18:00:04 +01:00
Jan Prochazka
fb1c2c61fb duplicator improvements 2023-02-16 17:25:54 +01:00
Jan Prochazka
b514f8ae35 using readline instead of line-reader-fixes freeze 2023-02-16 15:11:33 +01:00
Jan Prochazka
3114a05c3b save structure changes to jsonl file 2023-02-16 13:33:28 +01:00
Jan Prochazka
edf0637a35 change structure generates data commands 2023-02-16 13:14:56 +01:00
Jan Prochazka
cd1267b464 schema editing in dataset 2023-02-16 11:47:17 +01:00
Jan Prochazka
675ef6e593 v5.2.3-beta.6 2023-02-13 20:35:37 +01:00
Jan Prochazka
60bd3c157e fixed multi-db perspectives 2023-02-13 20:35:10 +01:00
Jan Prochazka
aceffd5681 v5.2.3-beta.5 2023-02-12 20:25:07 +01:00
Jan Prochazka
83f01c52f2 data duplicator style 2023-02-12 19:52:53 +01:00
Jan Prochazka
5e207a6c16 build fix 2023-02-12 12:44:40 +01:00
Jan Prochazka
10d5667c83 pg fix 2023-02-12 12:31:38 +01:00
Jan Prochazka
d1e1b2ce9c Merge branch 'develop' 2023-02-12 12:15:59 +01:00
Jan Prochazka
bb2f1399ba data duplicator runs in transaction 2023-02-12 12:14:07 +01:00
Jan Prochazka
5b6f90abc5 data duplicator - logs 2023-02-12 12:09:20 +01:00
Jan Prochazka
1d24562ead duplicator 2023-02-12 11:43:13 +01:00
Jan Prochazka
fb8174b3e9 delete cascade fix 2023-02-12 11:43:02 +01:00
Jan Prochazka
4e194539d9 fix 2023-02-11 10:24:52 +01:00
Jan Prochazka
b5e37053b8 data duplicator works in simple case 2023-02-11 10:17:10 +01:00
Jan Prochazka
f3dd187df7 useEditorData fix 2023-02-11 09:53:08 +01:00
Jan Prochazka
b5f504f3b1 data duplicator tab - configurator 2023-02-10 16:50:27 +01:00
Jan Prochazka
8df2a8a6df more mirroe archive commands 2023-02-10 15:14:02 +01:00
Jan Prochazka
dd46604069 correct saving jsonl data 2023-02-10 11:37:18 +01:00
Jan Prochazka
cc9402dd84 save archive algorithm 2023-02-10 11:25:18 +01:00
Jan Prochazka
be0f68fb7f editing changeset on archive file 2023-02-10 10:22:38 +01:00
Jan Prochazka
a3db8e2903 html & xml autio select highlighter #485 2023-02-08 07:34:28 +01:00
Jan Prochazka
87c29faadd html & xsml syntax highlight #485 2023-02-06 20:39:50 +01:00
Jan Prochazka
9bf610707e v5.2.3-beta.4 2023-02-06 20:30:24 +01:00
Jan Prochazka
28a568901a fixed rimraf usage 2023-02-06 20:29:58 +01:00
Jan Prochazka
1ba43af48d v5.2.3-beta.3 2023-02-05 20:23:29 +01:00
Jan Prochazka
356b623eaf downgraded rimraf, so that it passes old build 2023-02-05 20:23:16 +01:00
Jan Prochazka
85c3d6fe6f v5.2.3-beta.2 2023-02-05 20:07:56 +01:00
Jan Prochazka
d9eb0f0976 intelisense fix #484 2023-02-05 20:03:15 +01:00
Jan Prochazka
d61a7c54ce table data edit - shows editing mark 2023-02-05 19:58:45 +01:00
Jan Prochazka
cd000098f1 save table structure uses transaction 2023-02-05 19:24:22 +01:00
Jan Prochazka
e9a01a1ffd used transaction for save table data 2023-02-05 19:17:46 +01:00
Jan Prochazka
722789ca01 fix 2023-02-05 18:51:34 +01:00
Jan Prochazka
83ba530112 explicit order criteria only on MSSQL #436 2023-02-04 15:58:45 +01:00
Jan Prochazka
57fa9335d4 sort JSONL data & query results 2023-02-04 15:27:55 +01:00
Jan Prochazka
3babe95944 v5.2.3-beta.1 2023-02-04 09:46:29 +01:00
Jan Prochazka
aab1229220 fixed typo #481 2023-02-04 09:44:49 +01:00
Jan Prochazka
7b64587f6a fixed crash #452 2023-02-03 11:11:44 +01:00
Jan Prochazka
6a5157140e mysql default value #455 2023-02-03 11:06:59 +01:00
Jan Prochazka
47e0173f84 arm64 windows installer added to build #473 2023-02-03 10:01:11 +01:00
Jan Prochazka
8fe6cb1f71 fixed reading DB with mongo views #476 2023-02-03 09:59:53 +01:00
Jan Prochazka
dc6eff7f9e fixed show DB 2023-02-03 09:33:38 +01:00
Jan Prochazka
dad9e3ea48 changelog 2023-02-01 18:31:18 +01:00
Jan Prochazka
166c2254ec v5.2.2 2023-02-01 18:22:21 +01:00
Jan Prochazka
5ab4b9ee13 v5.2.2-alpha.13 2023-01-29 08:40:35 +01:00
Jan Prochazka
1c87b1b994 fixed dependency 2023-01-29 08:40:12 +01:00
Jan Prochazka
072c340d5f added missing dependency 2023-01-29 08:35:50 +01:00
Jan Prochazka
5bc7a8e763 v5.2.2-alpha.12 2023-01-29 08:30:09 +01:00
Jan Prochazka
655dec369f fix 2023-01-29 08:30:00 +01:00
Jan Prochazka
9356ef6667 dbmodel docs 2023-01-29 08:27:28 +01:00
Jan Prochazka
b3308dc389 v5.2.2-alpha.11 2023-01-28 20:19:30 +01:00
Jan Prochazka
7cbcafb6f7 dbmodel added to build 2023-01-28 20:19:16 +01:00
Jan Prochazka
adbb335062 v5.2.2-alpha.10 2023-01-28 20:15:05 +01:00
Jan Prochazka
bc1c827225 dbmodel commandline tool 2023-01-28 20:14:44 +01:00
Jan Prochazka
258338cd2e dbmodel tool initial import 2023-01-28 18:48:52 +01:00
Jan Prochazka
cf00af9e30 v5.2.2-beta.9 2023-01-28 16:33:08 +01:00
Jan Prochazka
0f515bb762 bigger timeout to yarn 2023-01-28 16:32:55 +01:00
Jan Prochazka
5ca3a66f17 remove call of snapcraft login 2023-01-28 16:30:43 +01:00
Jan Prochazka
4f857ab1f8 v5.2.2-beta.8 2023-01-28 13:45:54 +01:00
Jan Prochazka
5ed97079b1 fixed snapcraft login 2023-01-28 13:45:43 +01:00
Jan Prochazka
16408d85f8 support for binary values in filters #467 2023-01-28 12:57:17 +01:00
Jan Prochazka
cc388362d6 close query sessions after timeout #468 2023-01-28 11:40:52 +01:00
Jan Prochazka
079cac6eda use pinomin package 2023-01-28 10:22:12 +01:00
Jan Prochazka
a43522752c logger refactor 2023-01-28 09:17:57 +01:00
Jan Prochazka
dbcc732688 appname sent to connection - tedious 2023-01-27 16:42:01 +01:00
Jan Prochazka
3f525cacc1 appname added to pg connection string 2023-01-27 16:31:20 +01:00
Jan Prochazka
2fee308185 pinomin time field 2023-01-27 16:31:06 +01:00
Jan Prochazka
331c303e8f v5.2.2-beta.7 2023-01-27 15:40:50 +01:00
Jan Prochazka
7c8d225868 added missing file 2023-01-27 15:40:20 +01:00
Jan Prochazka
dd44798ff4 v5.2.2-beta.6 2023-01-27 15:38:36 +01:00
Jan Prochazka
2dd8749bc6 simplified logging 2023-01-27 15:37:16 +01:00
Jan Prochazka
174d7fde5c pinomin logger 2023-01-27 15:37:04 +01:00
Jan Prochazka
af3d271361 v5.2.2-beta.5 2023-01-23 20:11:59 +01:00
Jan Prochazka
17e83c700e try remove console logging for electron 2023-01-23 20:11:47 +01:00
Jan Prochazka
513fe6184a v5.2.2-beta.4 2023-01-23 19:41:42 +01:00
Jan Prochazka
b56f11156d try to fix electron errors after start 2023-01-23 19:41:31 +01:00
Jan Prochazka
80e8b210be handle errors when sending to subprocess #458 2023-01-23 19:28:05 +01:00
Jan Prochazka
d60687485b v5.2.2-beta.3 2023-01-23 18:26:53 +01:00
Jan Prochazka
7a62ef0cc3 remove handle electron errors 2023-01-23 18:26:43 +01:00
Jan Prochazka
0e58e94153 v5.2.2-beta.2 2023-01-22 19:29:00 +01:00
Jan Prochazka
8926e3bc84 Merge branch 'develop' 2023-01-22 19:27:57 +01:00
Jan Prochazka
ef62948b5a form view works for JSL data 2023-01-22 19:27:39 +01:00
Jan Prochazka
f014a4e6b4 added loadingformview 2023-01-22 19:12:32 +01:00
Jan Prochazka
e589a994fa form view cleanup 2023-01-22 18:31:00 +01:00
Jan Prochazka
6fdb9cc5c9 form works also for views 2023-01-22 18:26:49 +01:00
Jan Prochazka
11bb8faf91 form view - open reference 2023-01-22 18:22:18 +01:00
Jan Prochazka
98b26bb119 form view filters 2023-01-22 18:03:29 +01:00
Jan Prochazka
268c010a22 form view refactor - handle hiearchic columns 2023-01-22 17:27:13 +01:00
Jan Prochazka
6dd3945724 form view refactor - basically works 2023-01-22 16:26:48 +01:00
Jan Prochazka
ba644a37b7 removed hostname from logs 2023-01-22 12:35:11 +01:00
Jan Prochazka
e9322cc1ba fix 2023-01-22 12:27:10 +01:00
Jan Prochazka
f266acb807 #455 column default value help text 2023-01-22 12:21:12 +01:00
Jan Prochazka
9f66c5e28a logger info 2023-01-22 12:12:56 +01:00
Jan Prochazka
61d93fb9d9 Merge branch 'develop' 2023-01-22 12:07:06 +01:00
Jan Prochazka
c87e38fd17 log & report unhandled electron error 2023-01-22 11:56:09 +01:00
Jan Prochazka
7eb6357c8d #360 allow to set log level 2023-01-22 10:55:10 +01:00
Jan Prochazka
1cf02488b4 configuring logger for electron 2023-01-22 10:35:02 +01:00
Jan Prochazka
5249713a3c show logs from menu 2023-01-22 10:31:16 +01:00
Jan Prochazka
1bf8f38793 added process name to logger output 2023-01-22 10:12:46 +01:00
Jan Prochazka
e1f92fef13 pipe logs from forks into pino logger 2023-01-22 10:00:01 +01:00
Jan Prochazka
af01d95348 pino multistream - file logging 2023-01-22 09:50:35 +01:00
Jan Prochazka
d4f0882054 fixed error logging 2023-01-21 18:00:59 +01:00
Jan Prochazka
cc0f05168d defined logger caller 2023-01-21 17:49:16 +01:00
Jan Prochazka
4d93be61b5 PINO JSON logging 2023-01-21 17:32:28 +01:00
Jan Prochazka
dd230b008f Merge branch 'master' of github.com:dbgate/dbgate 2023-01-21 13:57:12 +01:00
Jan Prochazka
16238f8f94 Merge branch 'develop' 2023-01-21 13:56:55 +01:00
Jan Prochazka
20570c1988 Merge pull request #460 from ProjectInfinity/fix-sql-formatter
Update sql-formatter, fixes #450
2023-01-21 13:15:43 +01:00
Jan Prochazka
44dadcd256 fixed sqlite analyser 2023-01-21 11:01:19 +01:00
Jan Prochazka
cf07123f51 fixed msql analyser 2023-01-21 10:52:50 +01:00
Jan Prochazka
b56134d308 #457 fixed ctrl+tab 2023-01-21 10:40:13 +01:00
Jan Prochazka
f9f879272b analyser refactor + optimalization 2023-01-21 10:13:08 +01:00
Jan Prochazka
3dfae351a6 foreign key loading optimalization #451 2023-01-21 09:34:29 +01:00
Infinity
822482ab4e Update sql-formatter, fixes #450 2023-01-19 15:08:51 +01:00
Jan Prochazka
451f671426 v5.2.2-beta.1 2023-01-06 18:49:09 +01:00
Jan Prochazka
b06d747399 #451 loading fks on postgres cleanup & fix 2023-01-06 18:40:47 +01:00
Jan Prochazka
37eeaf0cce v5.2.1 2023-01-06 18:03:55 +01:00
Jan Prochazka
5f0ee80306 changelog 2023-01-06 18:03:44 +01:00
Jan Prochazka
d8f25c17f7 fix 2023-01-06 14:32:42 +01:00
Jan Prochazka
f6173335da v5.2.1-beta.3 2023-01-06 09:07:51 +01:00
Jan Prochazka
9fdc15b8aa used persmissions fixed 2023-01-06 09:06:54 +01:00
Jan Prochazka
77300f2078 fix login page 2023-01-06 08:34:27 +01:00
Jan Prochazka
3ab887f8e9 v5.2.1-beta.2 2023-01-05 10:19:03 +01:00
Jan Prochazka
5684eab3e2 OAuth scope added #407 2023-01-05 10:18:53 +01:00
Jan Prochazka
9ce743a8d3 v5.2.1-beta.1 2023-01-05 09:23:49 +01:00
Jan Prochazka
680c0057b1 fixed client_id param in oauth #407 2023-01-05 09:23:31 +01:00
Jan Prochazka
e9fffc063b changelog 2023-01-03 22:35:41 +01:00
Jan Prochazka
a0bc6f314c v5.2.0 2023-01-03 22:35:17 +01:00
Jan Prochazka
af1bb005e5 changelog 2023-01-02 19:53:22 +01:00
Jan Prochazka
34d891e935 changelog preparation 2023-01-02 19:52:45 +01:00
Jan Prochazka
dcccfe11c8 v5.1.7-alpha.14 2023-01-02 18:48:58 +01:00
Jan Prochazka
8823cff3a1 oracle build fix 2023-01-02 18:48:28 +01:00
Jan Prochazka
18320352ff v5.1.7-alpha.13 2023-01-02 18:35:35 +01:00
Jan Prochazka
d3292810f8 v5.1.7-beta.12 2023-01-01 19:55:59 +01:00
Jan Prochazka
7cd493e518 fixed(oracle) - removed incorrect query result row 2023-01-01 19:55:08 +01:00
Jan Prochazka
6c4b56a28b fixed loading materialized views in oracle 2023-01-01 19:50:19 +01:00
Jan Prochazka
0c795e33c3 commented out some console.log in oracle driver 2023-01-01 19:48:36 +01:00
Jan Prochazka
fd2e1e0cae v5.1.7-beta.11 2023-01-01 12:25:13 +01:00
Jan Prochazka
13fd7a0aad memoize connection folder expand state #425 2023-01-01 12:24:42 +01:00
Jan Prochazka
d5e240a701 rename, delete connection folder #425 2023-01-01 12:16:59 +01:00
Jan Prochazka
2151252032 fix 2023-01-01 10:29:54 +01:00
Jan Prochazka
cd175973d9 fixed file filters #445 2022-12-31 14:33:58 +01:00
Jan Prochazka
10789a75a8 force text display 2022-12-31 14:17:47 +01:00
Jan Prochazka
f775fbad29 force text display 2022-12-31 14:16:08 +01:00
Jan Prochazka
dbdb50f796 fix 2022-12-31 13:50:51 +01:00
Jan Prochazka
61a2002627 deep refresh on datagrid 2022-12-31 13:39:07 +01:00
Jan Prochazka
4d8e0d44d1 ALTER VIEW, ALTER PROCEDURE scripts 2022-12-31 13:05:16 +01:00
Jan Prochazka
e13808945c removed unused imports 2022-12-31 12:44:44 +01:00
Jan Prochazka
3aa7e6c022 map view refactor 2022-12-31 12:43:27 +01:00
Jan Prochazka
cb0a9770d2 map cell view improved 2022-12-31 12:29:47 +01:00
Jan Prochazka
4a2b33276d clone mongto rows without _id #404 2022-12-31 11:18:18 +01:00
Jan Prochazka
fb1cbc71f2 clear perspective cache reloads also patterns 2022-12-31 10:48:14 +01:00
Jan Prochazka
b8fcbbbc93 drag & drop memory in designer 2022-12-31 10:37:25 +01:00
Jan Prochazka
6b5d2114bf designer - column filter 2022-12-31 10:05:09 +01:00
Jan Prochazka
22b8b30768 Merge branch 'develop' 2022-12-30 19:10:46 +01:00
Jan Prochazka
175d85a462 fix 2022-12-30 19:10:10 +01:00
Jan Prochazka
ed69c55e91 Merge branch 'persubjoin' into develop 2022-12-30 18:55:32 +01:00
Jan Prochazka
637184a28e fix 2022-12-30 18:54:47 +01:00
Jan Prochazka
242e24b783 fix 2022-12-30 12:47:18 +01:00
Jan Prochazka
d407c72f78 handle $oid 2022-12-30 12:24:05 +01:00
Jan Prochazka
380ab2e69e fixes 2022-12-30 10:30:38 +01:00
Jan Prochazka
646a83b288 fix 2022-12-30 09:04:40 +01:00
Jan Prochazka
eb80eb1afa perspective subloading works 2022-12-29 20:25:21 +01:00
Jan Prochazka
b0f4965fb9 node load props impl - naive 2022-12-28 16:23:43 +01:00
Jan Prochazka
24b5e52666 refactor 2022-12-28 10:11:19 +01:00
Jan Prochazka
f45c9e38cb subcolumns in designer 2022-12-28 09:57:32 +01:00
Jan Prochazka
78b8fc0531 ux in DB login modal 2022-12-28 09:53:12 +01:00
Jan Prochazka
06d6815df4 readme 2022-12-26 09:34:50 +01:00
Jan Prochazka
4566654acb v5.1.7-beta.10 2022-12-25 19:59:39 +01:00
Jan Prochazka
eb3a7f7253 Merge branch 'askpassword' 2022-12-25 19:33:21 +01:00
Jan Prochazka
c340ac9112 disconnect command 2022-12-25 19:27:24 +01:00
Jan Prochazka
5c1c4e1fa6 single connection multi db layout 2022-12-25 18:57:08 +01:00
Jan Prochazka
bbb6c5e5f5 renamed singleDatabase => singleDbConnection 2022-12-25 18:01:36 +01:00
Jan Prochazka
54278f6276 single connection config 2022-12-25 17:52:59 +01:00
Jan Prochazka
a6fa116b5e renamed singleDatabaseMode to lockedDatabaseMode 2022-12-25 17:32:14 +01:00
Jan Prochazka
3792f1001e .env file 2022-12-25 17:32:12 +01:00
Jan Prochazka
8d1d6537a4 ask password works! 2022-12-25 17:12:12 +01:00
Jan Prochazka
783f26b500 structured reload trigger 2022-12-25 15:35:56 +01:00
Jan Prochazka
1eea117062 connection testing 2022-12-25 12:48:10 +01:00
Jan Prochazka
d66fc06403 ask password logic & modal 2022-12-25 10:21:19 +01:00
Jan Prochazka
fa13990189 single database mode 2022-12-23 11:03:03 +01:00
Jan Prochazka
45652cfc33 docs #444 2022-12-22 20:51:35 +01:00
Jan Prochazka
89219722a9 mongo: create collection backup 2022-12-22 20:49:22 +01:00
Jan Prochazka
b0d78250e1 row count info added to mongoDB 2022-12-22 17:35:46 +01:00
Jan Prochazka
0e92d51f3c formatKeyText called in CommandPalette 2022-12-22 17:17:11 +01:00
Jan Prochazka
535737ba72 code cleanup 2022-12-22 17:11:44 +01:00
Jan Prochazka
2213cda1c6 #246 fuzzy search in ctrl+p+capital search 2022-12-22 17:10:23 +01:00
Jan Prochazka
b712e3c6ae v5.1.7-beta.9 2022-12-18 18:57:22 +01:00
Jan Prochazka
f7f35ee306 fixed package version 2022-12-18 18:54:42 +01:00
Jan Prochazka
973015aed8 Merge remote-tracking branch 'rinie/oracle' 2022-12-18 18:47:05 +01:00
Jan Prochazka
2ae50ccbad Merge branch 'master' of github.com:dbgate/dbgate 2022-12-18 18:39:39 +01:00
Jan Prochazka
f2d8dfaf18 PR #440 - handle on startup 2022-12-18 18:39:35 +01:00
Jan Prochazka
b6afd24172 fix 2022-12-18 18:29:21 +01:00
Jan Prochazka
245ec58505 Merge branch 'profiler' 2022-12-18 18:26:25 +01:00
Jan Prochazka
1d8264c935 Merge pull request #440 from ProjectInfinity/maximize-button
Make maximize button reflect window state
2022-12-18 18:18:02 +01:00
Jan Prochazka
0ff4f0d7e9 profile refactoring, fixes 2022-12-18 17:03:47 +01:00
Jan Prochazka
3bbdc56309 max duration profiler measure 2022-12-18 16:18:56 +01:00
Jan Prochazka
2e37788471 profiler charts 2022-12-18 13:48:24 +01:00
Jan Prochazka
9a2631dc09 profiler charts 2022-12-18 12:29:21 +01:00
Jan Prochazka
dbfdaafb86 jsonl filtering fixes 2022-12-18 09:08:03 +01:00
Jan Prochazka
cf3df9cda3 short json value shown in grid 2022-12-17 20:22:42 +01:00
Jan Prochazka
274fcd339b archive file - open in profiler 2022-12-17 20:07:26 +01:00
Jan Prochazka
123e00ecbc mongo profiler formatter 2022-12-17 12:34:28 +01:00
Jan Prochazka
34a4f9adbf save profiler output to archive 2022-12-17 08:57:16 +01:00
Jan Prochazka
0e819bcc45 mongodb profiler 2022-12-16 14:52:49 +01:00
ProjectInfinity
570cb2d96b Make maximize button reflect window state 2022-12-16 14:44:26 +01:00
Jan Prochazka
c1ba758b01 mongo profile view - shows collection tab 2022-12-16 09:42:38 +01:00
Jan Prochazka
11daa56335 mongo filter: empty array, not empty array 2022-12-16 08:06:37 +01:00
Jan Prochazka
a9257cf4f8 camel case search 2022-12-15 20:37:38 +01:00
Jan Prochazka
1a2acd764d improved editor margin #422 2022-12-15 18:50:23 +01:00
Jan Prochazka
27b0af6408 Merge branch 'master' of github.com:dbgate/dbgate 2022-12-15 17:39:24 +01:00
Jan Prochazka
3c63738809 fixed missing versioned tables #433 2022-12-15 17:38:02 +01:00
Jan Prochazka
9305e767cd Merge pull request #437 from horaciod/patch-1
Typo in export action
2022-12-15 17:30:24 +01:00
Jan Prochazka
2fddf32e54 fixed broken F5 2022-12-15 17:15:19 +01:00
Jan Prochazka
469fd76f89 upgrade dbgate-query-splitter 2022-12-15 17:10:01 +01:00
Horacio Degiorgi
1f682d91c9 Typo in export action 2022-12-14 10:51:31 -03:00
Jan Prochazka
87c3b39ae9 v5.1.7-beta.8 2022-12-09 15:50:07 +01:00
Jan Prochazka
a1032138da Merge branch 'summary' 2022-12-09 15:49:22 +01:00
Jan Prochazka
9fa6155cd9 refresh server summary 2022-12-09 15:48:48 +01:00
Jan Prochazka
ea77b4fc1a view profile data 2022-12-09 15:41:42 +01:00
Jan Prochazka
61dc9da3f0 set mongo profiling 2022-12-09 15:14:21 +01:00
Jan Prochazka
9d6fe2460f fix 2022-12-09 08:47:26 +01:00
Jan Prochazka
e6ac878b74 mongo summary improved 2022-12-08 19:51:01 +01:00
Jan Prochazka
ceea1a9047 mongo server summary 2022-12-07 22:05:47 +01:00
Jan Prochazka
f7bd12881e v5.1.7-beta.7 2022-12-06 19:55:28 +01:00
Jan Prochazka
4d74626e7f API url fix 2022-12-06 19:55:18 +01:00
Jan Prochazka
a2884a580f v5.1.7-beta.6 2022-12-06 19:39:14 +01:00
Jan Prochazka
c8c7df3691 static content server without authorization 2022-12-06 19:38:52 +01:00
Jan Prochazka
9f8ac81038 v5.1.7-beta.5 2022-12-06 18:48:50 +01:00
Jan Prochazka
ae8c5c0cc1 v5.1.7-beta.4 2022-11-28 21:21:37 +01:00
Jan Prochazka
3dc63507ad fix 2022-11-28 21:21:24 +01:00
Jan Prochazka
6ddb8b8bf9 fixed mongoUrl regression 2022-11-28 21:07:48 +01:00
Jan Prochazka
688434d25b v5.1.7-beta.3 2022-11-27 20:33:16 +01:00
Jan Prochazka
df2074173b js fix 2022-11-27 20:33:09 +01:00
Jan Prochazka
b825167687 v5.1.7-beta.2 2022-11-27 19:45:10 +01:00
Jan Prochazka
621181d532 Merge branch 'oracle' 2022-11-27 19:44:28 +01:00
Jan Prochazka
c2b6b08105 Merge branch 'oauth' 2022-11-27 19:43:40 +01:00
Jan Prochazka
8489c171f3 AD_ALLOWED_LOGINS support 2022-11-27 18:32:01 +01:00
Jan Prochazka
592865b16e configurable token lifetime 2022-11-27 11:06:33 +01:00
Jan Prochazka
012d3ec2e1 logout button from not logged page 2022-11-27 10:56:50 +01:00
Jan Prochazka
d84adcca5d more robust oauth 2022-11-27 10:43:25 +01:00
Jan Prochazka
b1ae7d53b9 forms login 2022-11-26 11:21:37 +01:00
Jan Prochazka
9a5287725b login WIP 2022-11-25 16:59:41 +01:00
Jan Prochazka
5ccd724166 support for acticve directory #261 2022-11-25 16:38:17 +01:00
Jan Prochazka
5e4c286427 ignore auth .env 2022-11-25 16:15:41 +01:00
Jan Prochazka
70413b954b login page 2022-11-25 13:36:18 +01:00
Rinie Kervel
97cb9f2752 just before own repo 2022-11-22 10:40:38 +01:00
Rinie Kervel
61287c5480 try native plugin 2022-11-21 15:36:38 +01:00
Jan Prochazka
9c1c008b0d v5.1.7-beta.1 2022-11-20 17:08:19 +01:00
Jan Prochazka
896cc21386 oracledb added to native dependencies 2022-11-20 17:07:21 +01:00
Jan Prochazka
a7a8ea053b fill native modules - oracledb 2022-11-20 17:03:55 +01:00
Jan Prochazka
07b2a3e923 oauth disabling API 2022-11-17 20:09:27 +01:00
Jan Prochazka
94a91d5fed better oauth handle 2022-11-17 19:55:01 +01:00
Jan Prochazka
576fc2062c fix 2022-11-17 19:26:39 +01:00
Jan Prochazka
37a8783751 oauth working, but cycling sometimes 2022-11-17 12:43:38 +01:00
Jan Prochazka
f42d78b2fb oauth returns access token 2022-11-14 21:20:58 +01:00
Jan Prochazka
522170d5c3 changelog 2022-11-14 19:44:30 +01:00
Jan Prochazka
3891e7768d v5.1.6 2022-11-14 19:35:58 +01:00
Jan Prochazka
792fa75ccd v5.1.6-beta.7 2022-11-13 18:39:30 +01:00
Jan Prochazka
cbd3f1bae9 fixed number format 2022-11-13 18:39:13 +01:00
Jan Prochazka
cd92231769 v5.1.6-beta.6 2022-11-13 17:56:19 +01:00
Jan Prochazka
ecad1ae01b imrpoved closing inactive sessions 2022-11-13 17:21:47 +01:00
Jan Prochazka
dc576e6ced changed timeouts for connection cleanup 2022-11-13 11:59:57 +01:00
Jan Prochazka
6cca81f8f1 cleanup of not used sessions 2022-11-13 11:52:31 +01:00
Jan Prochazka
a9f1f19696 wincert renamed 2022-11-12 20:17:21 +01:00
Jan Prochazka
390ddac75b v5.1.6-beta.5 2022-11-12 19:43:30 +01:00
Jan Prochazka
e2e7c6f06b cert update 2022-11-12 19:43:20 +01:00
Jan Prochazka
3a3d0683d5 v5.1.6-beta.4 2022-11-12 18:49:08 +01:00
Jan Prochazka
d5534dcf07 added win.publisherName 2022-11-12 18:48:30 +01:00
Jan Prochazka
b0a86f9f4a v5.1.6-beta.3 2022-11-12 18:11:54 +01:00
Jan Prochazka
b833a30148 wincert changed 2022-11-12 18:11:42 +01:00
Jan Prochazka
d9c1bbaa39 v5.1.6-beta.2 2022-11-10 16:43:14 +01:00
Jan Prochazka
4b74dbbd68 fixed #416: Double click does not maximize window do on MacOS 2022-11-10 13:41:55 +01:00
Jan Prochazka
9bcc61551c #406 show/hide reult window 2022-11-10 12:35:25 +01:00
Jan Prochazka
ed71ef312d #406 keyboard shortcut to show/hide sidebar 2022-11-10 11:36:31 +01:00
Jan Prochazka
4fa043b7e5 fix 2022-11-10 11:06:40 +01:00
Jan Prochazka
83725dd349 fixed loading perspective 2022-11-10 10:51:51 +01:00
Jan Prochazka
4e25b71b06 removed folder text field 2022-11-10 10:11:47 +01:00
Jan Prochazka
607ae7c872 #274 allow to create folder 2022-11-10 10:10:53 +01:00
Jan Prochazka
66ade5823f ts fix 2022-11-10 09:46:54 +01:00
Jan Prochazka
ebfa0a1939 allow drop on group #274 2022-11-10 09:45:41 +01:00
Rinie Kervel
48b1e28ee1 fix lowercase for tablelist do not always convert column names to lower 2022-11-07 12:09:50 +01:00
Jan Prochazka
909591404f v5.1.6-beta.1 2022-11-07 07:43:09 +01:00
Jan Prochazka
7a5f2a70ad fixed/removed svelte warnings 2022-11-06 13:32:17 +01:00
Jan Prochazka
d41b254058 import type refactor 2022-11-06 13:31:13 +01:00
Jan Prochazka
435d06ffb9 rollup config - ignore some svelte warnings 2022-11-06 13:29:58 +01:00
Jan Prochazka
f4a4eb7f9e ts const fix 2022-11-06 13:29:00 +01:00
Jan Prochazka
9910bbead3 Merge pull request #411 from qlaffont/master
FEAT: Folders support
2022-11-05 10:45:10 +01:00
Jan Prochazka
cb619a0fe0 drag & drop into/from connection folder 2022-11-05 10:36:41 +01:00
Quentin Laffont
b0d61f974c feat(app): able to set a parent 2022-11-04 10:05:17 +01:00
Quentin Laffont
8c051ff5f7 Merge pull request #1 from dbgate/master
update
2022-11-04 08:08:05 +01:00
Jan Prochazka
f713a4b183 import type refactor 2022-11-03 16:45:28 +01:00
Jan Prochazka
6c7e263f0e Merge branch 'master' of github.com:dbgate/dbgate 2022-11-03 16:45:05 +01:00
Jan Prochazka
ec3bfb4fae Screen postcss fix 2022-11-03 16:41:31 +01:00
Jan Prochazka
712ec8e6ee Merge pull request #409 from notz/fix-mongodb-url-with-ssh
Fix connection to mongodb via database url if a ssh tunnel is used
2022-11-03 13:41:42 +01:00
Gernot Pansy
4da0b25f44 Fix connection to mongodb via database url if a ssh tunnel is used
* Replaces the the port with the tunnel port in the url
2022-11-03 11:01:24 +01:00
Jan Prochazka
9b60b7a003 fix perspective error 2022-11-03 09:18:41 +01:00
Quentin Laffont
8ed73195c5 chore(app): add node version 2022-11-03 09:13:54 +01:00
Quentin Laffont
c69fcd5eff feat(connections): able to save parent Id 2022-11-03 08:35:49 +01:00
Rinie Kervel
a0cefbc1ca merge dbgate master and test drivers 2022-10-30 08:32:53 +01:00
Rinie Kervel
5c0c145fd6 Merge branch 'dbgate:master' into oracle 2022-10-30 08:13:51 +01:00
Jan Prochazka
310774db3b v5.1.5 2022-10-15 22:10:11 +02:00
Jan Prochazka
1dd166b563 Merge branch 'develop' 2022-10-15 22:09:45 +02:00
Jan Prochazka
0497f541cb changelog 2022-10-15 22:08:57 +02:00
Jan Prochazka
42333a97b8 v5.1.5-beta.4 2022-10-13 15:36:24 +02:00
Jan Prochazka
494c3c8e4a remap Command+H on mac #390 2022-10-13 15:28:50 +02:00
Jan Prochazka
69a87bc076 perspective expand fix 2022-10-13 15:22:23 +02:00
Jan Prochazka
bf4eb19ef5 perspective fixes 2022-10-13 15:01:45 +02:00
Jan Prochazka
225518df3e show json icon in perspectives 2022-10-13 14:09:08 +02:00
Jan Prochazka
0028240552 perspective fix 2022-10-13 13:49:48 +02:00
Jan Prochazka
44be1bdd11 menu label 2022-10-13 12:36:48 +02:00
Rinie Kervel
64168577ab zap fastmode as IN =OBJECT_ID_CONDITION does not work 2022-10-13 12:05:08 +02:00
Jan Prochazka
e0703b1bae Merge branch 'master' into develop 2022-10-13 11:02:40 +02:00
Jan Prochazka
a240681d6d auto view json #395 2022-10-13 11:02:25 +02:00
Jan Prochazka
f5906587db perspectives: prevent multi-load 2022-10-13 10:52:10 +02:00
Rinie Kervel
51952ecfdd Oracle driver first data 2022-10-11 17:04:38 +02:00
Jan Prochazka
dc0001a8cd sql case configuration #389 2022-10-08 21:14:18 +02:00
Jan Prochazka
f19835203f fixed pager component #388 2022-10-08 09:08:42 +02:00
Jan Prochazka
2a2debbb88 fix nested mongo id as $oid #387 2022-10-08 09:00:19 +02:00
Jan Prochazka
23cb3a4b12 table&database ctx menu improvement 2022-10-08 08:34:31 +02:00
Jan Prochazka
13d4d34453 cond-disable ER diagram and query design #386 2022-10-08 08:09:45 +02:00
Jan Prochazka
2adca64159 socketPath configurable with env variables #358 2022-10-04 20:58:10 +02:00
Jan Prochazka
18519b5519 v5.1.5-beta.3 2022-10-02 21:04:41 +02:00
Jan Prochazka
4ddea55d23 Merge branch 'master' into develop 2022-10-02 21:04:25 +02:00
Jan Prochazka
5858061349 v5.1.5-beta.2 2022-10-02 21:03:22 +02:00
Jan Prochazka
d86a5c0cb4 fix 2022-10-02 21:02:23 +02:00
Jan Prochazka
c712005e33 v5.1.5-beta.1 2022-10-02 21:00:29 +02:00
Jan Prochazka
7e28e2257e Merge branch 'master' into develop 2022-10-02 20:59:54 +02:00
Jan Prochazka
d0c7d591c8 don't tag BETA docker images with version 2022-10-02 20:58:52 +02:00
Jan Prochazka
17b73a58c8 changelog 2022-10-02 20:56:05 +02:00
Jan Prochazka
d765591e8c v5.1.4 2022-10-02 20:52:53 +02:00
Jan Prochazka
be0aeeb2c8 perspective - pattern for SQL sources 2022-10-02 20:52:22 +02:00
Jan Prochazka
23b345c898 perspective mongo fixes 2022-10-02 17:48:03 +02:00
Jan Prochazka
1d85a17533 fix 2022-10-02 15:43:00 +02:00
Jan Prochazka
7a3c46b691 perspective display - mongo nested objects 2022-10-02 12:01:07 +02:00
Jan Prochazka
d647d30258 perspective nosql test 2022-10-02 11:30:05 +02:00
Jan Prochazka
8b511a0532 removed commented code 2022-10-02 10:19:53 +02:00
Jan Prochazka
ccb52e9b58 fix 2022-10-02 10:19:01 +02:00
Jan Prochazka
f60e1190c8 perspectives: mongo join works 2022-10-02 09:44:52 +02:00
Jan Prochazka
da5dd7ac62 perspective mongo sort 2022-10-01 19:18:18 +02:00
Jan Prochazka
08abec7c3e perspective mongo condition 2022-10-01 18:50:54 +02:00
Jan Prochazka
b3839def32 mongo perspective stuff - basic skeleton works 2022-10-01 17:48:47 +02:00
Jan Prochazka
efe15bf0bb mongo perspective fixes 2022-10-01 16:44:34 +02:00
Jan Prochazka
f9e167fc7b perspective data pattern 2022-10-01 14:43:25 +02:00
Jan Prochazka
b35e8fcdf4 v5.1.4-beta.11 2022-09-29 19:42:50 +02:00
Jan Prochazka
4bdd988682 fix 2022-09-29 19:42:34 +02:00
Jan Prochazka
94f21472be v5.1.4-beta.10 2022-09-29 19:36:18 +02:00
Jan Prochazka
dd33d96ef6 close tabs question 2022-09-29 19:35:58 +02:00
Jan Prochazka
7604889b72 unsaved file marker 2022-09-29 13:58:09 +02:00
Jan Prochazka
1382461bdc current query part fix 2022-09-29 11:01:17 +02:00
Jan Prochazka
833f029ab5 drop database #384 2022-09-29 09:58:35 +02:00
Jan Prochazka
04d39f6646 single docker builder 2022-09-28 20:47:32 +02:00
Jan Prochazka
4de8a5b038 v5.1.4-docker.9 2022-09-28 20:44:32 +02:00
Jan Prochazka
1dfdeed018 actions fix 2022-09-28 20:42:59 +02:00
Jan Prochazka
4892e46795 v5.1.4-docker.8 2022-09-28 20:37:42 +02:00
Jan Prochazka
5aff68d313 single docker build file 2022-09-28 20:37:32 +02:00
Jan Prochazka
cdd4382266 v5.1.4-docker.7 2022-09-28 20:12:37 +02:00
Jan Prochazka
bbd00ac94d multi platform 2022-09-28 20:12:25 +02:00
Jan Prochazka
dba3183c94 v5.1.4-docker.6 2022-09-28 20:09:19 +02:00
Jan Prochazka
a2906cca9d docker build fix 2022-09-28 20:09:05 +02:00
Jan Prochazka
140291696b v5.1.4-docker.5 2022-09-28 19:58:29 +02:00
Jan Prochazka
975643fb24 fix 2022-09-28 19:58:14 +02:00
Jan Prochazka
bf9a933fb1 v5.1.4-docker.4 2022-09-28 19:44:09 +02:00
Jan Prochazka
643b792069 docker for arm platform #383 2022-09-28 19:43:54 +02:00
Jan Prochazka
b4d0ccbd8c v5.1.4-docker.3 2022-09-28 19:40:06 +02:00
Jan Prochazka
c9bf949d02 version fix 2022-09-28 19:37:57 +02:00
Jan Prochazka
074390ac11 v5.1.4-docker.2 2022-09-28 19:22:56 +02:00
Jan Prochazka
45e54475d0 docker build 2022-09-28 19:22:41 +02:00
Jan Prochazka
f157fc77d4 docker fix 2022-09-28 19:18:30 +02:00
Jan Prochazka
dac1110404 v5.1.4-docker.1 2022-09-28 19:02:43 +02:00
Jan Prochazka
da00e1c228 docker build with tags 2022-09-28 19:02:13 +02:00
Jan Prochazka
9ed1cdf4b7 redis key separator fix #379 2022-09-28 18:18:03 +02:00
Jan Prochazka
18b7792370 customizable redis key separator #379 2022-09-28 18:11:46 +02:00
Jan Prochazka
53b6b71a29 non default schema name in tab title 2022-09-28 16:56:47 +02:00
Jan Prochazka
b2204e1d77 using gutte3r decorations for active query part 2022-09-28 13:21:21 +02:00
Jan Prochazka
e7ac7558ca better UX of code editor 2022-09-28 13:04:50 +02:00
Jan Prochazka
c5a7f458ba better editor SQL splitting 2022-09-28 12:24:06 +02:00
Jan Prochazka
8ce5e68c0d typo 2022-09-28 09:19:53 +02:00
Jan Prochazka
e9256fe20e changelog 2022-09-26 20:19:07 +02:00
Jan Prochazka
5913788035 v5.1.3 2022-09-26 19:53:21 +02:00
Rinie Kervel
4939b74179 get version result and login from oracle 2022-09-26 17:54:24 +02:00
Jan Prochazka
6c9c4be311 v5.1.3-beta.2 2022-09-25 20:11:22 +02:00
Jan Prochazka
1454ddacb8 fix 2022-09-25 20:10:56 +02:00
Jan Prochazka
2b26779ea8 fixes + sqlite error line number 2022-09-25 20:06:34 +02:00
Jan Prochazka
7781ad69cf sql error line number - postgres 2022-09-25 19:50:31 +02:00
Jan Prochazka
1a7f06342f query error markers 2022-09-25 19:45:47 +02:00
Jan Prochazka
2f820d8dac sql editor - play icon to execute sql fragment 2022-09-25 14:43:10 +02:00
Jan Prochazka
1535dfd407 youtube link 2022-09-25 11:42:30 +02:00
Jan Prochazka
3fe7d652b2 multiline json editing 2022-09-25 09:29:38 +02:00
Jan Prochazka
7fc8b2901b multiline cell editor #378 #371 #359 2022-09-25 08:58:39 +02:00
Jan Prochazka
a56f59ceba readme 2022-09-23 21:36:24 +02:00
Jan Prochazka
2ac1072357 v5.1.3-beta.1 2022-09-22 18:46:33 +02:00
Jan Prochazka
24c26a6d87 UX fix 2022-09-22 18:46:14 +02:00
Jan Prochazka
83693e9f2c perspectives: show row count 2022-09-22 18:23:04 +02:00
Jan Prochazka
59efdd735c truncate table context menu #333 2022-09-22 16:01:52 +02:00
Jan Prochazka
41afd177ef editing multiline cell value #378 #371 #359 2022-09-22 15:05:05 +02:00
Jan Prochazka
0137b191b9 changelog 2022-09-19 19:36:59 +02:00
Jan Prochazka
054b90c90d changelog 2022-09-19 19:35:47 +02:00
Jan Prochazka
a46526cbc8 v5.1.2 2022-09-19 19:29:28 +02:00
Jan Prochazka
35c42d0a83 v5.1.2-beta.5 2022-09-18 19:50:59 +02:00
Jan Prochazka
6e2ecd0b05 perspective: show undefined
cells
2022-09-18 18:51:10 +02:00
Jan Prochazka
a98a4617ae #371 wip 2022-09-18 18:43:06 +02:00
Jan Prochazka
1a716f0bce sql condition in filter dialog #369 2022-09-18 14:57:30 +02:00
Jan Prochazka
973f64f4d7 custom SQL condition #369 2022-09-17 21:42:20 +02:00
Jan Prochazka
a89c6810aa query design ordering more posibilities #372 2022-09-17 10:06:23 +02:00
Jan Prochazka
3d45b00a7c form view cursor commands on toolbar #370 2022-09-17 09:59:37 +02:00
Jan Prochazka
f93524e24f condition editor-allow combine NULL,NOT NULL #368 2022-09-17 09:48:54 +02:00
Jan Prochazka
9aded740ca #373 fixed mongodb export 2022-09-17 09:25:12 +02:00
Jan Prochazka
66f30ff26e v5.1.2-beta.4 2022-09-15 16:49:12 +02:00
Jan Prochazka
4ced94f070 perspective image display 2022-09-15 16:48:57 +02:00
Jan Prochazka
fe61e5e631 json view in perspective improvement 2022-09-15 16:04:44 +02:00
Jan Prochazka
24b0d278fd v5.1.2-beta.3 2022-09-12 20:46:42 +02:00
Jan Prochazka
de5b075ba5 perspective inline json view 2022-09-12 20:45:26 +02:00
Jan Prochazka
1665c014e1 perspective fix 2022-09-12 20:16:45 +02:00
Jan Prochazka
586a06da91 v5.1.2-beta.2 2022-09-11 20:14:31 +02:00
Jan Prochazka
eb1eb18163 reorder query design columns #362 2022-09-11 10:24:10 +02:00
Jan Prochazka
1983576b2f fixed editing sort order in query designer #363 2022-09-11 08:42:32 +02:00
Jan Prochazka
ffbb91678c v5.1.2-beta.1 2022-09-08 15:49:54 +02:00
Jan Prochazka
0293766bad connecting via socket for mysql and postgres #358 2022-09-08 14:23:13 +02:00
Jan Prochazka
5eda39cb62 rows affected info for postgresql 2022-09-08 11:04:43 +02:00
Jan Prochazka
b7c8a60c19 affected rows info for MySQL & MariaDB #361 2022-09-08 10:51:06 +02:00
Jan Prochazka
51101d91ea screenshot 2022-09-05 20:53:46 +02:00
Jan Prochazka
cc9acf71ce readm screenshot 2022-09-05 20:51:12 +02:00
Jan Prochazka
d27f8644d8 readme screenshot 2022-09-05 20:43:23 +02:00
Jan Prochazka
347448e3c2 changelog 2022-09-05 20:13:31 +02:00
Jan Prochazka
0a008a760b v5.1.1 2022-09-05 20:08:45 +02:00
Jan Prochazka
462be9e2bd v5.1.1-beta.5 2022-09-04 20:31:42 +02:00
Jan Prochazka
f078872c5b postgre rename column fixed #350 2022-09-04 11:00:36 +02:00
Jan Prochazka
fdecef7e78 analyse computed columns on ms sql #354 2022-09-04 10:04:24 +02:00
Jan Prochazka
8acafbbd6e Clear filter hotkey 2022-09-04 08:55:42 +02:00
Jan Prochazka
5b8d70747f fixed datetime null condition #356 2022-09-04 08:47:58 +02:00
Jan Prochazka
c9a9c7d0f7 v5.1.1-beta.4 2022-09-01 19:19:23 +02:00
Jan Prochazka
50eb5012b1 perspective: place nodes not in tree 2022-09-01 19:18:46 +02:00
Jan Prochazka
917c2f49a0 temp root in perspectives 2022-09-01 19:12:03 +02:00
Jan Prochazka
5724067974 fix 2022-09-01 18:35:50 +02:00
Jan Prochazka
428de38b41 grayed nodes in perspective designer 2022-09-01 17:51:40 +02:00
Jan Prochazka
9e73e16b7f parent filter icon in designer 2022-09-01 17:38:43 +02:00
Jan Prochazka
1e91097bf2 splitter doesn't recreate children on collapse 2022-09-01 16:20:35 +02:00
Jan Prochazka
61f82be9f3 fix 2022-09-01 15:53:44 +02:00
Jan Prochazka
91e1c83a91 perspective - fixed work with specific DB 2022-09-01 15:50:03 +02:00
Jan Prochazka
e8452704eb perspective collapsible splitters 2022-09-01 14:55:20 +02:00
Jan Prochazka
357fcbdf47 collapsible vertical splitter 2022-09-01 14:48:29 +02:00
Jan Prochazka
02abb4f512 add fk reference from designer 2022-09-01 14:22:54 +02:00
Jan Prochazka
14f71e80d3 skip loading data without columns 2022-09-01 13:35:51 +02:00
Jan Prochazka
fdcf1c4c9a fixed perspective tests 2022-09-01 12:03:19 +02:00
Jan Prochazka
97e96aaba6 some perspective tests fixed 2022-09-01 11:59:25 +02:00
Jan Prochazka
174b0efd2e perspective checked fix 2022-09-01 11:21:31 +02:00
Jan Prochazka
eab5f4fe5e new perspective command 2022-09-01 11:18:25 +02:00
Jan Prochazka
a910e91a91 fix perspective 2022-09-01 10:56:58 +02:00
Jan Prochazka
3e83a69ef7 handle invalid perspective format 2022-09-01 10:10:19 +02:00
Jan Prochazka
e3b833927d v5.1.1-beta.3 2022-08-30 21:40:40 +02:00
Jan Prochazka
6582c7831e perspective: fixed menu not not rooted nodes 2022-08-30 21:40:27 +02:00
Jan Prochazka
0d2169c996 v5.1.1-beta.2 2022-08-29 22:40:14 +02:00
Jan Prochazka
e64d013fee perspective: view fix, UX 2022-08-29 22:37:04 +02:00
Jan Prochazka
c1627b8546 v5.1.1-beta.1 2022-08-28 14:10:24 +02:00
Jan Prochazka
2f74eab048 better paint perspective 2022-08-28 14:08:13 +02:00
Jan Prochazka
f7a269383f perspective designer table icon 2022-08-28 13:35:22 +02:00
Jan Prochazka
5f9156995b perspective set root command 2022-08-28 13:22:41 +02:00
Jan Prochazka
f886b8c95d perspective alias 2022-08-28 13:11:42 +02:00
Jan Prochazka
2284264a92 perspective filter menu 2022-08-28 12:10:53 +02:00
Jan Prochazka
f405db7685 show sort in perspective deisgner and tree 2022-08-28 12:02:38 +02:00
Jan Prochazka
14110cb6db reimplemented filter, sort for new model 2022-08-28 10:50:12 +02:00
Jan Prochazka
1e347f6535 perspective fixes 2022-08-28 08:42:07 +02:00
Jan Prochazka
0813f4387d arrange fix 2022-08-28 08:09:54 +02:00
Jan Prochazka
894a864110 perspective fixes 2022-08-28 07:52:36 +02:00
Jan Prochazka
4e799885b5 perspective default columns - before refactor 2022-08-28 07:25:02 +02:00
Jan Prochazka
650f9a3db9 fix 2022-08-27 20:11:20 +02:00
Jan Prochazka
6b5e33d97e default columns processor 2022-08-27 19:50:51 +02:00
Jan Prochazka
24923db199 ensure node config 2022-08-27 19:17:49 +02:00
Jan Prochazka
80faf0fd68 alias fix 2022-08-27 18:19:54 +02:00
Jan Prochazka
33b11eef38 custom joins 2022-08-27 18:07:49 +02:00
Jan Prochazka
b6a0fe6713 node checked & column checked distuingish 2022-08-27 16:02:15 +02:00
Jan Prochazka
2b68a6e1de FK secondary checkbox 2022-08-27 15:26:11 +02:00
Jan Prochazka
e124291267 perspective designer - checking columns 2022-08-27 12:27:07 +02:00
Jan Prochazka
1a16d7c69e changed perspective checked logic 2022-08-27 11:57:54 +02:00
Jan Prochazka
6cb2616d87 designer customization 2022-08-27 10:42:58 +02:00
Jan Prochazka
395863da3f simplify designer settings 2022-08-27 10:17:11 +02:00
Jan Prochazka
fec2df9d2f perspective tree layout 2022-08-27 10:06:06 +02:00
Jan Prochazka
9e3a457ef5 perspective designer auto arrange 2022-08-27 09:19:17 +02:00
Jan Prochazka
728ad21d2f perspective arrange button 2022-08-26 19:17:35 +02:00
Jan Prochazka
d2f18bc048 checkedNodes => checkedColumns 2022-08-26 18:25:40 +02:00
Jan Prochazka
0ae7939f93 table designer checkbox 2022-08-26 18:23:43 +02:00
Jan Prochazka
7ac0b907e2 perspective designer 2022-08-25 21:11:07 +02:00
Jan Prochazka
1bd4b77744 fix 2022-08-25 18:31:55 +02:00
Jan Prochazka
5e4ae3208b fixed bug in perspective display + fixed test 2022-08-25 18:01:00 +02:00
Jan Prochazka
daf7629f5f perspective designer WIP 2022-08-25 15:20:16 +02:00
Jan Prochazka
aeceb34d19 perspective refactor WIP 2022-08-25 13:18:55 +02:00
Jan Prochazka
2a98918857 open qdesign and perspective file #349 2022-08-25 09:13:02 +02:00
Jan Prochazka
ce9d583989 added restore-terminals config 2022-08-19 15:47:55 +02:00
Jan Prochazka
7c87baf451 custom editor size #345 2022-08-19 15:10:04 +02:00
Jan Prochazka
f80c6fec99 readme 2022-08-08 20:49:32 +02:00
Jan Prochazka
b04af4c5e3 typo 2022-08-08 20:41:06 +02:00
Jan Prochazka
fe65193189 typo 2022-08-08 20:40:32 +02:00
Jan Prochazka
a75e463ef5 v5.1.0 2022-08-08 20:20:31 +02:00
Jan Prochazka
7eb59ad3a0 changelog - docs link 2022-08-08 19:50:07 +02:00
Jan Prochazka
7a9f8a460f v5.1.0-beta.4 2022-08-08 19:47:03 +02:00
Jan Prochazka
289752c023 Merge branch 'develop' 2022-08-08 19:46:39 +02:00
Jan Prochazka
98f2c06c21 perspectives: default column algorithm 2022-08-08 19:46:12 +02:00
Jan Prochazka
530b1cade3 perspective UX 2022-08-08 19:40:50 +02:00
Jan Prochazka
65aa8fb4e3 fixed multi - database structure store 2022-08-08 19:37:40 +02:00
Jan Prochazka
4c0f17a0b2 changelog 2022-08-07 20:35:04 +02:00
Jan Prochazka
e4371c526b v5.1.0-beta.3 2022-08-07 20:29:04 +02:00
Jan Prochazka
e39f0a1f4b perspective parent filter fix 2022-08-07 20:28:41 +02:00
Jan Prochazka
842f77d02b v5.1.0-beta.2 2022-08-07 19:37:04 +02:00
Jan Prochazka
2571e6ac7e fixed tests 2022-08-07 19:36:37 +02:00
Jan Prochazka
1599a7ea01 v5.1.0-beta.1 2022-08-07 19:30:25 +02:00
Jan Prochazka
cb1d81b586 perspectives: parent filter switch in filters 2022-08-07 19:30:00 +02:00
Jan Prochazka
339588b8a0 parent filter implementation 2022-08-07 19:16:23 +02:00
Jan Prochazka
1731b7e4a3 parentFilter - declarative support 2022-08-07 18:12:10 +02:00
Jan Prochazka
5418bb932c removed commented code 2022-08-07 16:49:16 +02:00
Jan Prochazka
6154b4c780 perspective:removed filterInfos,using only filters 2022-08-07 16:48:27 +02:00
Jan Prochazka
3f9bd100e1 perspective refactor 2022-08-07 16:40:37 +02:00
Jan Prochazka
b5c6ddce59 perspective: filter this value, open filtered table 2022-08-07 16:03:20 +02:00
Jan Prochazka
51c72efb34 fixed NTLM auth in SQL server #305 2022-08-07 15:13:10 +02:00
Jan Prochazka
00df20e350 imrpoved editing data with keyboard #331 2022-08-07 12:01:02 +02:00
Jan Prochazka
f3a7e3af74 option to skip table save confirmation #329 2022-08-07 11:21:08 +02:00
Jan Prochazka
04c37c2b4f v5.0.10-beta.14 2022-08-07 10:42:39 +02:00
Jan Prochazka
12df0993c0 fixed sqlite3 native module version 2022-08-07 10:42:27 +02:00
Jan Prochazka
ac3ec5c11e #324 fixed column type syntax for mysql 2022-08-07 10:17:32 +02:00
Jan Prochazka
b565e981e4 v5.0.10-beta.13 2022-08-07 10:05:17 +02:00
Jan Prochazka
f7ada698e4 fix 2022-08-07 10:04:32 +02:00
Jan Prochazka
bc4c146389 remove msnodesqlv8 from mac+linux build 2022-08-07 10:02:11 +02:00
Jan Prochazka
7c80ca1374 Revert "try to fix mac build"
This reverts commit 1974243ed5.
2022-08-07 09:52:59 +02:00
Jan Prochazka
8c5cc7dcc1 v5.0.10-beta.12 2022-08-07 09:03:18 +02:00
Jan Prochazka
1974243ed5 try to fix mac build 2022-08-07 09:02:51 +02:00
Jan Prochazka
71c9071cb8 default action on connection click: connect #332 2022-08-07 08:44:31 +02:00
Jan Prochazka
c28e55132a v5.0.10-beta.11 2022-08-07 08:28:38 +02:00
Jan Prochazka
2b2a4debd4 sqlite prebuild for mac 2022-08-07 08:28:21 +02:00
Jan Prochazka
563a35560b save perspective to file 2022-08-06 17:43:49 +02:00
Jan Prochazka
cc019281d4 perspective custom joins supports views 2022-08-06 17:03:48 +02:00
Jan Prochazka
86d7d61cc5 perspectives: custom join over different databases 2022-08-06 16:44:37 +02:00
Jan Prochazka
aff1fe0b3d perspectives: prefer not circular lookups 2022-08-06 15:30:49 +02:00
Jan Prochazka
137631b5b5 sort references 2022-08-06 14:37:00 +02:00
Jan Prochazka
090ffa064d perspective: open table ctx menu 2022-08-06 14:05:18 +02:00
Jan Prochazka
f77cc1023b perspective column filter 2022-08-06 13:49:05 +02:00
Jan Prochazka
c6dbb31748 perspective filters supports lookup 2022-08-06 13:24:51 +02:00
Jan Prochazka
ae6c486db5 perspective load fix 2022-08-06 11:37:44 +02:00
Jan Prochazka
9a2c12d558 perspective context menu 2022-08-05 20:55:04 +02:00
Jan Prochazka
1ed01e9839 perspective cell highlight 2022-08-05 20:17:49 +02:00
Jan Prochazka
25d2c129cd perspective ctx menu 2022-08-05 19:55:14 +02:00
Jan Prochazka
7dc7af0cdb v5.0.10-beta.10 2022-08-04 21:32:20 +02:00
Jan Prochazka
80fea3b01b style 2022-08-04 21:32:06 +02:00
Jan Prochazka
97dc92e413 perspectives:add to filter ctx menu 2022-08-04 08:26:35 +02:00
Jan Prochazka
9051ba2ee1 filter list in perspective 2022-08-04 08:16:22 +02:00
Jan Prochazka
7dcbe6c7c1 v5.0.10-beta.9 2022-08-03 20:51:22 +02:00
Jan Prochazka
e6fe8a6379 persp: fixed loading 2022-08-03 20:31:29 +02:00
Jan Prochazka
b793e4131d perspectives: scroll optimalization 2022-08-03 20:23:36 +02:00
Jan Prochazka
b737eaac13 v5.0.10-beta.8 2022-07-31 21:10:14 +02:00
Jan Prochazka
cb5cce2ea3 custom joins working on the same DB 2022-07-31 21:09:58 +02:00
Jan Prochazka
b05d260caa perspective custom join editing & removing 2022-07-31 20:53:22 +02:00
Jan Prochazka
091e91556d custom join dialog 2022-07-31 20:09:48 +02:00
Jan Prochazka
2b4120435b perspectives support views 2022-07-31 16:56:09 +02:00
Jan Prochazka
c8d031e2c4 removed debug code 2022-07-31 16:10:57 +02:00
Jan Prochazka
ac07b7e1ba code cleanup 2022-07-31 15:30:06 +02:00
Jan Prochazka
bf51f45934 removed perspective intersection observer 2022-07-31 15:28:04 +02:00
Jan Prochazka
fe31cfb552 css fix 2022-07-31 12:28:08 +02:00
Jan Prochazka
d505be09ca perspectives - sort 2022-07-31 12:24:06 +02:00
Jan Prochazka
44668b8017 perspective sort - divided by table 2022-07-31 12:22:13 +02:00
Jan Prochazka
452dba7f32 perspective sorting 2022-07-31 12:10:56 +02:00
Jan Prochazka
7694864fe7 perspective loading indicator 2022-07-31 10:12:22 +02:00
Jan Prochazka
37d5c6fbf9 cell data formatting in perspectives 2022-07-31 09:40:20 +02:00
Jan Prochazka
802f231e43 ts fix 2022-07-31 08:52:10 +02:00
Jan Prochazka
53c39e6a43 run perspectives test on CI 2022-07-31 08:49:25 +02:00
Jan Prochazka
65f550023a removed obsolete code 2022-07-31 08:47:32 +02:00
Jan Prochazka
abe7a20960 perspective - changed display table algorithm 2022-07-31 08:46:24 +02:00
Jan Prochazka
d686206fe2 perspective display test WIP 2022-07-31 08:11:04 +02:00
Jan Prochazka
27b2fdb507 Copy as JSON in JSON tab 2022-07-31 08:10:19 +02:00
Jan Prochazka
88f522084d show table name in perspective 2022-07-30 09:21:04 +02:00
Jan Prochazka
8472c8be79 fixed filter parser test (upgraded jestm, ts-jest) 2022-07-30 08:39:35 +02:00
Jan Prochazka
03f8a93dd0 perspective fix 2022-07-30 08:16:07 +02:00
Jan Prochazka
2889f79120 v5.0.10-beta.7 2022-07-30 08:08:12 +02:00
Jan Prochazka
8a312181a3 upgraded all dependencies 2022-07-30 08:07:23 +02:00
Jan Prochazka
e7236de078 v5.0.10-beta.6 2022-07-30 07:35:44 +02:00
Jan Prochazka
1fe2269b11 upgraded better-sqlite3 2022-07-30 07:35:34 +02:00
Jan Prochazka
10ea8ca3a6 v5.0.10-beta.5 2022-07-29 21:32:54 +02:00
Jan Prochazka
491d24984d upgraded electron to v17 2022-07-29 21:31:45 +02:00
Jan Prochazka
b0279dd315 display fix 2022-07-29 21:16:07 +02:00
Jan Prochazka
9d6b581809 remove commented code 2022-07-29 21:08:50 +02:00
Jan Prochazka
3f748df1ec perspective: fixed some table scenarios 2022-07-29 21:04:09 +02:00
Jan Prochazka
7ca835765c v5.0.10-beta.4 2022-07-28 21:41:01 +02:00
Jan Prochazka
a76530155d filter child tables 2022-07-28 21:02:24 +02:00
Jan Prochazka
96b82b690e v5.0.10-beta.3 2022-07-28 20:43:26 +02:00
Jan Prochazka
d3a40e52fc perspective defaults - FK columns 2022-07-28 20:43:03 +02:00
Jan Prochazka
513b2ba42f default checked columns 2022-07-28 20:35:39 +02:00
Jan Prochazka
d23371f642 ref loading ref column 2022-07-28 20:03:48 +02:00
Jan Prochazka
5ac6e12c3e v5.0.10-beta.2 2022-07-28 19:24:26 +02:00
Jan Prochazka
4468c0ed3b fix perspective refresh 2022-07-28 19:23:17 +02:00
Jan Prochazka
06bd9bcabe perspective - show error, ability to reset filters 2022-07-28 18:57:15 +02:00
Jan Prochazka
66d15abcab last row render fix 2022-07-28 18:46:33 +02:00
Jan Prochazka
3bdb5c0152 perspective filters 2022-07-25 21:42:01 +02:00
Jan Prochazka
f504283002 use position:sticky for table header 2022-07-25 20:48:33 +02:00
Jan Prochazka
f07c7909ef fix 2022-07-25 20:37:15 +02:00
Jan Prochazka
c809f58349 v5.0.10-beta.1 2022-07-24 21:25:06 +02:00
Jan Prochazka
3e91ecd141 perspective filters control 2022-07-24 21:17:52 +02:00
Jan Prochazka
857185a78b Merge branch 'master' into develop 2022-07-24 21:10:19 +02:00
Jan Prochazka
c189c12cae changelog 2022-07-24 21:07:02 +02:00
Jan Prochazka
96106e6aac refresh perspective command 2022-07-24 15:56:29 +02:00
Jan Prochazka
088ca231f3 uniqe binding values 2022-07-24 15:34:26 +02:00
Jan Prochazka
5395d1343b nested incomplete loading fix 2022-07-24 15:16:02 +02:00
Jan Prochazka
d48c34a4a5 perspctives: nested incremental loading 2022-07-24 14:23:56 +02:00
Jan Prochazka
53ee1d87c2 Merge branch 'master' into develop 2022-07-24 10:17:47 +02:00
Jan Prochazka
b5d97c8181 v5.0.9 2022-07-24 10:17:17 +02:00
Jan Prochazka
28e06166e0 perspectives - prepare for nested incremental load 2022-07-21 18:10:43 +02:00
Jan Prochazka
8f1343bc42 perspectives: fixed incremental loading 2022-07-21 17:14:27 +02:00
Jan Prochazka
2080a23b69 incremental loading 2022-07-21 17:05:07 +02:00
Jan Prochazka
d71294621b perspective cache - basic design 2022-07-21 15:43:17 +02:00
Jan Prochazka
0f6ec420d2 delete commented code 2022-07-21 12:33:44 +02:00
Jan Prochazka
35152a2796 perspective loader class 2022-07-21 12:33:29 +02:00
Jan Prochazka
1abfab950e perspectives: added data provider layer 2022-07-21 11:26:44 +02:00
Jan Prochazka
6e6d0bb616 Merge branch 'master' into develop 2022-07-21 09:33:26 +02:00
Jan Prochazka
93e264e9ec v5.0.9-beta.1 2022-07-21 09:33:11 +02:00
Jan Prochazka
29257f9bf9 added missing dependency 2022-07-21 09:32:03 +02:00
Jan Prochazka
8dd90ce5e4 fixed problem with SSE #323 2022-07-21 09:31:58 +02:00
Jan Prochazka
f2f7421971 new diagram, new query design added to menu 2022-07-21 09:31:52 +02:00
Jan Prochazka
8a10beef52 added missing dependency 2022-07-21 09:31:02 +02:00
Jan Prochazka
df33b43e90 fixed problem with SSE #323 2022-07-21 07:49:55 +02:00
Jan Prochazka
153cba3779 new diagram, new query design added to menu 2022-07-21 07:27:28 +02:00
Jan Prochazka
8f110355c4 Merge branch 'master' into develop 2022-07-18 22:46:31 +02:00
Jan Prochazka
b570f873fe changelog 2022-07-18 20:51:38 +02:00
Jan Prochazka
c07e26c036 v5.0.8 2022-07-18 20:46:40 +02:00
Jan Prochazka
995bc6f16a v5.0.8-beta.4 2022-07-17 19:16:27 +02:00
Jan Prochazka
5b4339889f OR in group filter 2022-07-17 19:15:42 +02:00
Jan Prochazka
2b4d5c026e Merge branch 'master' into develop 2022-07-14 21:26:07 +02:00
Jan Prochazka
1becb89ff0 code format 2022-07-14 05:10:11 +02:00
Jan Prochazka
4d7365828e perspective - styles 2022-07-01 14:54:41 +02:00
Jan Prochazka
29ccb09ba6 perspectives: loading only neccessary columns 2022-07-01 10:24:35 +02:00
Jan Prochazka
eadd3feba0 fixed header fixes 2022-07-01 09:55:54 +02:00
Jan Prochazka
93269fe314 perspectives: fixed table header 2022-07-01 08:00:21 +02:00
Jan Prochazka
34ca4c501a fixed table header 2022-06-30 21:46:45 +02:00
Jan Prochazka
34084d0e94 perspective styling 2022-06-30 21:14:56 +02:00
Jan Prochazka
07fc551383 perspective row spans 2022-06-30 21:01:27 +02:00
Jan Prochazka
b0eed05a1a perspective rows 2022-06-30 19:13:01 +02:00
Jan Prochazka
8228afd725 perspective rows WIP 2022-06-30 18:10:50 +02:00
Jan Prochazka
301222d118 perspectives: show nested columns 2022-06-30 10:38:03 +02:00
Jan Prochazka
b6b75f0743 perspectives WIP 2022-06-23 16:50:56 +02:00
Jan Prochazka
aca92f3889 perspectives: render simple table 2022-06-23 16:04:05 +02:00
Jan Prochazka
4672540f82 Merge branch 'master' into develop 2022-06-23 15:33:14 +02:00
Jan Prochazka
2fcc4b1ff0 perspectives WIP 2022-06-23 14:24:06 +02:00
Jan Prochazka
7ca8880c3c Merge branch 'master' into develop 2022-06-23 11:18:36 +02:00
Jan Prochazka
42badf17eb perspective - load hiearchic JSON 2022-06-20 22:14:48 +02:00
Jan Prochazka
2ec3c2c24f perspective tre shows dependencies 2022-06-18 08:46:40 +02:00
Jan Prochazka
f3ab06d3b8 refactor 2022-06-18 08:00:00 +02:00
Jan Prochazka
2b78a8dcae perspective WIP 2022-06-17 22:30:10 +02:00
Jan Prochazka
75bf0e53fc perspectives WIP 2022-06-16 17:05:42 +02:00
Jan Prochazka
ff4dd18c1b Merge branch 'master' into develop 2022-06-16 13:37:12 +02:00
Jan Prochazka
24191870e8 Merge branch 'master' into develop 2022-06-13 19:47:06 +02:00
Jan Prochazka
18710bc67d v5.0.4 2022-06-13 19:31:31 +02:00
Jan Prochazka
02e8bba999 Merge branch 'master' into develop 2022-06-12 20:20:37 +02:00
Jan Prochazka
0cb4ec54bc perspective WIP 2022-06-12 19:30:54 +02:00
568 changed files with 32799 additions and 11515 deletions

View File

@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macOS-10.15, windows-2022, ubuntu-18.04]
os: [macos-12, windows-2022, ubuntu-22.04]
# os: [macOS-10.15]
steps:
@@ -23,16 +23,27 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 14.x
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x
- name: yarn adjustPackageJson
run: |
yarn adjustPackageJson
- name: yarn set timeout
run: |
yarn config set network-timeout 100000
- name: yarn install
run: |
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillNativeModulesElectron
run: |
yarn fillNativeModulesElectron
@@ -40,7 +51,7 @@ jobs:
run: |
yarn fillPackagedPlugins
- name: Install Snapcraft
if: matrix.os == 'ubuntu-18.04'
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
run: |
@@ -48,27 +59,24 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_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'
if: matrix.os == 'ubuntu-22.04'
run: |
snapcraft login --with snapcraft.login
snapcraft upload --release=beta app/dist/*.snap
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Copy artifacts
run: |
@@ -86,6 +94,7 @@ jobs:
mv app/dist/*.exe artifacts/ || true
mv app/dist/*.zip artifacts/ || true
mv app/dist/*.tar.gz artifacts/ || true
mv app/dist/*.AppImage artifacts/ || true
mv app/dist/*.deb artifacts/ || true
mv app/dist/*.snap artifacts/ || true

View File

@@ -16,8 +16,8 @@ jobs:
strategy:
fail-fast: false
matrix:
# os: [ubuntu-18.04, windows-2016]
os: [macOS-10.15, windows-2022, ubuntu-18.04]
# os: [ubuntu-22.04, windows-2016]
os: [macos-12, windows-2022, ubuntu-22.04]
steps:
- name: Context
@@ -27,10 +27,16 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 14.x
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x
- name: yarn adjustPackageJson
run: |
yarn adjustPackageJson
- name: yarn set timeout
run: |
yarn config set network-timeout 100000
- name: yarn install
run: |
# yarn --version
@@ -39,6 +45,11 @@ jobs:
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillNativeModulesElectron
run: |
yarn fillNativeModulesElectron
@@ -46,7 +57,7 @@ jobs:
run: |
yarn fillPackagedPlugins
- name: Install Snapcraft
if: matrix.os == 'ubuntu-18.04'
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
run: |
@@ -54,31 +65,28 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_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'
if: matrix.os == 'ubuntu-22.04'
run: |
snapcraft login --with snapcraft.login
snapcraft upload --release=stable app/dist/*.snap
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Copy artifacts
run: |
@@ -96,6 +104,7 @@ jobs:
mv app/dist/*.exe artifacts/ || true
mv app/dist/*.zip artifacts/ || true
mv app/dist/*.tar.gz artifacts/ || true
mv app/dist/*.AppImage artifacts/ || true
mv app/dist/*.deb artifacts/ || true
mv app/dist/*.dmg artifacts/ || true
@@ -132,12 +141,12 @@ jobs:
mv app/dist/dbgate-pad.xml artifacts/ || true
- name: Copy latest-linux.yml
if: matrix.os == 'ubuntu-18.04'
if: matrix.os == 'ubuntu-22.04'
run: |
mv app/dist/latest-linux.yml artifacts/latest-linux.yml || true
- name: Copy latest-mac.yml
if: matrix.os == 'macOS-10.15'
if: matrix.os == 'macos-12'
run: |
mv app/dist/latest-mac.yml artifacts/latest-mac.yml || true

View File

@@ -1,58 +0,0 @@
name: Docker image BETA
# on: [push]
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-docker.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04]
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: yarn install
run: |
# yarn --version
# yarn config set network-timeout 300000
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: Prepare docker image
run: |
yarn run prepare:docker
- name: Build docker image
run: |
docker build ./docker -t dbgate
- name: Push docker image
run: |
docker tag dbgate dbgate/dbgate:beta
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker push dbgate/dbgate:beta
- name: Build alpine docker image
run: |
docker build ./docker -t dbgate -f docker/Dockerfile-alpine
- name: Push alpine docker image
run: |
docker tag dbgate dbgate/dbgate:beta-alpine
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker push dbgate/dbgate:beta-alpine

View File

@@ -1,17 +1,11 @@
name: Docker image
# on: [push]
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
# on:
# push:
# branches:
# - production
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-docker.[0-9]+'
jobs:
build:
@@ -20,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-18.04]
os: [ubuntu-22.04]
steps:
- name: Context
@@ -30,32 +24,79 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 14.x
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
dbgate/dbgate
flavor: |
latest=false
tags: |
type=raw,value=beta,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }}
type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
- name: Docker alpine meta
id: alpmeta
uses: docker/metadata-action@v4
with:
images: |
dbgate/dbgate
flavor: |
latest=false
tags: |
type=raw,value=beta-alpine,enable=${{ contains(github.ref_name, '-docker.') || contains(github.ref_name, '-beta.') }}
type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x
- name: yarn install
run: |
# yarn --version
# yarn config set network-timeout 300000
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
- name: Prepare docker image
run: |
yarn run prepare:docker
- name: Build docker image
run: |
docker build ./docker -t dbgate
- name: Push docker image
run: |
docker tag dbgate dbgate/dbgate
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker push dbgate/dbgate
- name: Build alpine docker image
run: |
docker build ./docker -t dbgate -f docker/Dockerfile-alpine
- name: Push alpine docker image
run: |
docker tag dbgate dbgate/dbgate:alpine
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker push dbgate/dbgate:alpine
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
context: ./docker
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Build and push alpine
uses: docker/build-push-action@v3
with:
push: true
context: ./docker
file: ./docker/Dockerfile-alpine
tags: ${{ steps.alpmeta.outputs.tags }}
platforms: linux/amd64,linux/arm64,linux/arm/v7

View File

@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-18.04]
os: [ubuntu-22.04]
steps:
- name: Context
@@ -30,10 +30,10 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 14.x
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 14.x
node-version: 18.x
- name: Configure NPM token
env:
@@ -49,6 +49,12 @@ jobs:
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
- name: Publish types
working-directory: packages/types
run: |
@@ -94,6 +100,11 @@ jobs:
run: |
npm publish
- name: Publish dbmodel
working-directory: packages/dbmodel
run: |
npm publish
- name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv
run: |
@@ -138,3 +149,8 @@ jobs:
working-directory: plugins/dbgate-plugin-redis
run: |
npm publish
- name: Publish dbgate-plugin-oracle
working-directory: plugins/dbgate-plugin-oracle
run: |
npm publish

View File

@@ -8,7 +8,7 @@ on:
jobs:
test-runner:
runs-on: ubuntu-latest
container: node:14.18
container: node:18
steps:
- name: Context
@@ -31,6 +31,11 @@ jobs:
run: |
cd packages/filterparser
yarn test:ci
- name: Datalib (perspective) tests
if: always()
run: |
cd packages/datalib
yarn test:ci
- uses: tanmen/jest-reporter@v1
if: always()
with:
@@ -43,6 +48,12 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/datalib/result.json
action-name: Datalib (perspectives) test results
services:
postgres:

1
.node-version Normal file
View File

@@ -0,0 +1 @@
16.14.2

20
.vscode/restore-terminals.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"terminals": [
{
"splitTerminals": [
{
"name": "lib",
"commands": ["yarn lib"]
},
{
"name": "web",
"commands": ["yarn start:web"]
},
{
"name": "api",
"commands": ["yarn start:api"]
}
]
}
]
}

View File

@@ -1,3 +1,6 @@
{
"jestrunner.jestCommand": "node_modules/.bin/cross-env DEVMODE=1 LOCALTEST=1 node_modules/.bin/jest"
"jestrunner.jestCommand": "node_modules/.bin/cross-env DEVMODE=1 LOCALTEST=1 node_modules/.bin/jest",
"cSpell.words": [
"dbgate"
]
}

View File

@@ -8,6 +8,258 @@ Builds:
- linux - application for linux
- win - application for Windows
### 5.3.4
- FIXED: On blank system does not start (window does not appear) #862
- FIXED: Missing Execute, Export bar #861
### 5.3.3
- FIXED: The application Window is not visible when openning after changing monitor configuration. #856
- FIXED: Multi column filter is broken for Postgresql #855
- ADDED: Do not display internal timescaledb objects in postgres databases #839
- FIXED: When in splitview mode and Clicking "Refresh" button on the right side, will refresh the left side, and not the right side #810
- FIXED: Cannot filter by uuid field in psql #538
### 5.3.1
- FIXED: Column sorting on query tab not working #819
- FIXED: Postgres Connection stays in "Loading database structure" until reloading the page #826
- FIXED: Cannot read properties of undefined (reading 'length') on Tables #824
- FIXED: Redshift doesn't show tables when connected #816
### 5.3.0
- CHANGED: New Oracle driver, much better Oracle support. Works now also in docker distribution
- FIXED: Connection to oracle with service name #809
- ADDED: Connect to redis using a custom username #807
- FIXED: Unable to open SQL files #797
- FIXED: MongoDB query without columns #811
- ADDED: Switch connection for opened file #814
### 5.2.9
- FIXED: PostgresSQL doesn't show tables when connected #793 #805
- FIXED: MongoDB write operations fail #798 #802
- FIXED: Elecrron app logging losed most of log messages
- FIXED: Connection error with SSH tunnel
- ADDED: option to disable autoupgrades (with --disable-auto-upgrade)
- ADDED: Send error context to github gist
### 5.2.8
- FIXED: file menu save and save as not working
- FIXED: query editor on import/export screen overlaps with selector
- FIXED: Fixed inconsistencies in max/unmaximize window buttons
- FIXED: shortcut for select all
- FIXED: download with auth header
- CHANGED: Upgraded database drivers for mysql, postgres, sqlite, mssql, mongo, redis
- CHANGED: Upgraded electron version (now using v30)
- ADDED: Vim keyboard bindings for editor
- FIXED: Correctly select the save folder for dump
- ADDED: enum + set for mysql (#693)
- FIXED: localStorageGabageCollector not working
- FIXED: Encoding error when opening Unicode query files
- ADDED: Add copy/paste to query tab and database list
- ADDED: Add copy name to table list
- FIXED: Make TabControl scrollable (#730)
- ADDED: Add copy to column list
- FIXED: Problems with SQLite + glibc in docker containers
- ADDED: Button for discard/reset changes (#759)
- FIXED: Don't show error dialog when subprocess fails, as DbGate handles this correctly (#751, #746, #542, #272)
### 5.2.7
- FIXED: fix body overflow when context menu height great than viewport #592
- FIXED: Pass signals in entrypoint.sh #596
- FIXED: Remove missing links to jenasoft #625
- FIXED: add API headers on upload call #627
- FIXED: Disabled shell scripting for NPM distribution by default
- FIXED: Fixed data import from files #633
- FIXED: Fixed showing GPS positions #575
- CHANGED: Improved stability of electron client on Windows and Mac (fewer EPIPE errors)
### 5.2.6
- FIXED: DbGate creates a lot of .tmp.node files in the temp directory #561
- FIXED: Typo in datetimeoffset dataType #556
- FIXED: SQL export is using the wrong hour formatting #537
- FIXED: Missing toolstrip and adds up to 200% zoom to diagram view #524
- FIXED: MongoDB password could contain special characters #560
### 5.2.5
- ADDED: Split Windows #394
- FIXED: Postgres index asc/desc #514
- FIXED: Excel export not working since 5.2.3 #511
- ADDED: Include macOS specific app icon #494
- FIXED: Resizing window resets window contents #479
- FIXED: Solved some minor problems with widget collapsing
### 5.2.4
- FIXED: npm version crash (#508)
### 5.2.3
- ADDED: Search entire table (multi column filter) #491
- ADDED: OracleDB - connection to toher than default ports #496
- CHANGED: OracleDB - status of support set to experimental
- FIXED: OracleDB database URL - fixes: Connect to default Oracle database #489
- ADDED: HTML, XML code highlighting for Edit cell value #485
- FIXED: Intellisense - incorrect alias after ORDER BY clause #484
- FIXED: Typo in SQL-Generator #481
- ADDED: Data duplicator #480
- FIXED: MongoDB - support for views #476
- FIXED: "SQL:CREATE TABLE" generated SQL default value syntax errors #455
- FIXED: Crash when right-clicking on tables #452
- FIXED: View sort #436
- ADDED: Arm64 version for Windows #473
- ADDED: Sortable query results and data archive
- CHANGED: Use transactions for saving table data
- CHANGED: Save table structure uses transactions
- ADDED: Table data editing - shows editing mark
- ADDED: Editing data archive files
- FIXED: Delete cascade options when using more than 2 tables
- ADDED: Save to current archive commands
- ADDED: Current archive mark is on status bar
- FIXED: Changed package used for parsing JSONL files when browsing - fixes backend freezing
- FIXED: SSL option for mongodb #504
- REMOVED: Data sheet editor
- FIXED: Creating SQLite autoincrement column
- FIXED: Better error reporting from exports/import/dulicator
- CHANGED: Optimalizede OracleDB analysing algorithm
- ADDED: Mutli column filter for perspectives
- FIXED: Fixed some scenarios using tables from different DBs
- FIXED: Sessions with long-running queries are not killed
### 5.2.2
- FIXED: Optimalized load DB structure for PostgreSQL #451
- ADDED: Auto-closing query connections after configurable (15 minutes default) no-activity interval #468
- ADDED: Set application-name connection parameter (for PostgreSQL and MS SQL) for easier identifying of DbGate connections
- ADDED: Filters supports binary IDs #467
- FIXED: Ctrl+Tab works (switching tabs) #457
- FIXED: Format code supports non-standard letters #450
- ADDED: New logging system, log to file, ability to reduce logging #360 (using https://www.npmjs.com/package/pinomin)
- FIXED: crash on Windows and Mac after system goes in suspend mode #458
- ADDED: dbmodel standalone NPM package (https://www.npmjs.com/package/dbmodel) - deploy database via commandline tool
### 5.2.1
- FIXED: client_id param in OAuth
- ADDED: OAuth scope parameter
- FIXED: login page - password was not sent, when submitting by pressing ENTER
- FIXED: Used permissions fix
- FIXED: Export modal - fixed crash when selecting different database
### 5.2.0
- ADDED: Oracle database support #380
- ADDED: OAuth authentification #407
- ADDED: Active directory (Windows) authentification #261
- ADDED: Ask database credentials when login to DB
- ADDED: Login form instead of simple authorization (simple auth is possible with special configuration)
- FIXED: MongoDB - connection uri regression
- ADDED: MongoDB server summary tab
- FIXED: Broken versioned tables in MariaDB #433
- CHANGED: Improved editor margin #422
- ADDED: Implemented camel case search in all search boxes
- ADDED: MonhoDB filter empty array, not empty array
- ADDED: Maximize button reflects window state
- ADDED: MongoDB - database profiler
- CHANGED: Short JSON values are shown directly in grid
- FIXED: Fixed filtering nested fields in NDJSON viewer
- CHANGED: Improved fuzzy search after Ctrl+P #246
- ADDED: MongoDB: Create collection backup
- ADDED: Single database mode
- ADDED: Perspective designer supports joins from MongoDB nested documents and arrays
- FIXED: Perspective designer joins on MongoDB ObjectId fields
- ADDED: Filtering columns in designer (query designer, diagram designer, perspective designer)
- FIXED: Clone MongoDB rows without _id attribute #404
- CHANGED: Improved cell view with GPS latitude, longitude fields
- ADDED: SQL: ALTER VIEW and SQL:ALTER PROCEDURE scripts
- ADDED: Ctrl+F5 refreshes data grid also with database structure #428
- ADDED: Perspective display modes: text, force text #439
- FIXED: Fixed file filters #445
- ADDED: Rename, remove connection folder, memoize opened state after app restart #425
- FIXED: Show SQLServer alter store procedure #435
### 5.1.6
- ADDED: Connection folders support #274
- ADDED: Keyboard shortcut to hide result window and show/hide the side toolbar #406
- ADDED: Ability to show/hide query results #406
- FIXED: Double click does not maximize window on MacOS #416
- FIXED: Some perspective rendering errors
- FIXED: Connection to MongoDB via database URL info SSH tunnel is used
- CHANGED: Updated windows code signing certificate
- ADDED: Query session cleanup (kill query sessions, if browser tab is closed)
- CHANGED: More strict timeouts to kill database and server connections (reduces resource consumption)
### 5.1.5
- ADDED: Support perspectives for MongoDB - MongoDB query designer
- ADDED: Show JSON content directly in the overview #395
- CHANGED: OSX Command H shortcut for hiding window #390
- ADDED: Uppercase Autocomplete Suggestions #389
- FIXED: Record view left/right arrows cause start record number to be treated as string #388
- FIXED: MongoDb ObjectId behaviour not consistent in nested objects #387
- FIXED: demo.dbgate.org - beta version crash 5.1.5-beta.3 #386
- ADDED: connect via socket - configurable via environment variables #358
### 5.1.4
- ADDED: Drop database commands #384
- ADDED: Customizable Redis key separator #379
- ADDED: ARM support for docker images
- ADDED: Version tags for docker images
- ADDED: Better SQL command splitting and highlighting
- ADDED: Unsaved marker for SQL files
### 5.1.3
- ADDED: Editing multiline cell values #378 #371 #359
- ADDED: Truncate table #333
- ADDED: Perspectives - show row count
- ADDED: Query - error markers in gutter area
- ADDED: Query - ability to execute query elements from gutter
- FIXED: Correct error line numbers returned from queries
### 5.1.2
- FIXED: MongoDb any export function does not work. #373
- ADDED: Query Designer short order more flexibility #372
- ADDED: Form View move between records #370
- ADDED: Custom SQL conditions in query designer and table filtering #369
- ADDED: Query Designer filter eq to X or IS NULL #368
- FIXED: Query designer, open a saved query lost sort order #363
- ADDED: Query designer reorder columns #362
- ADDED: connect via socket #358
- FIXED: Show affected rows after UPDATE/DELETE/INSERT #361
- ADDED: Perspective cell formatters - JSON, image
- ADDED: Perspectives - cells without joined data are gray
### 5.1.1
- ADDED: Perspective designer
- FIXED: NULL,NOT NULL filter datatime columns #356
- FIXED: Recognize computed columns on SQL server #354
- ADDED: Hotkey for clear filter #352
- FIXED: Change column type on Postgres #350
- ADDED: Ability to open qdesign file #349
- ADDED: Custom editor font size #345
- ADDED: Ability to open perspective files
### 5.1.0
- ADDED: Perspectives (docs: https://dbgate.org/docs/perspectives.html )
- CHANGED: Upgraded SQLite engine version (driver better-sqlite3: 7.6.2)
- CHANGED: Upgraded ElectronJS version (from version 13 to version 17)
- CHANGED: Upgraded all dependencies with current available minor version updates
- CHANGED: By default, connect on click #332˝
- CHANGED: Improved keyboard navigation, when editing table data #331
- ADDED: Option to skip Save changes dialog #329
- FIXED: Unsigned column doesn't work correctly. #324
- FIXED: Connect to MS SQL with domain user now works also under Linux and Mac #305
### 5.0.9
- FIXED: Fixed problem with SSE events on web version
- ADDED: Added menu command "New query designer"
- ADDED: Added menu command "New ER diagram"
### 5.0.8
- ADDED: SQL Server - support using domain logins under Linux and Mac #305
- ADDED: Permissions for connections #318
- ADDED: Ability to change editor front #308
- ADDED: Custom expression in query designer #306
- ADDED: OR conditions in query designer #321
- ADDED: Ability to configure settings view environment variables #304
### 5.0.7
- FIXED: Fixed some problems with SSH tunnel (upgraded SSH client) #315
- FIXED: Fixed MognoDB executing find query #312

687
LICENSE
View File

@@ -1,21 +1,674 @@
MIT License
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (c) 2021 Jan Prochazka
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
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:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
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.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,3 +1,7 @@
This project is licensed under the GPLv3 License. See the LICENSE file for full text of the GPLv3 license.
The original project was licensed under the MIT License, and the following notice applies to the original code:
MIT License
Copyright (c) 2021 Jan Prochazka
@@ -18,4 +22,4 @@ 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.
SOFTWARE.

View File

@@ -12,7 +12,7 @@ DbGate is cross-platform database manager.
It's designed to be simple to use and effective, when working with more databases simultaneously.
But there are also many advanced features like schema compare, visual query designer, chart visualisation or batch export and import.
DbGate is licensed under MIT license and is completely free.
DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
* 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/)
@@ -22,6 +22,7 @@ DbGate is licensed under MIT license and is completely free.
* MySQL
* PostgreSQL
* SQL Server
* Oracle
* MongoDB
* Redis
* SQLite
@@ -66,21 +67,23 @@ DbGate is licensed under MIT license and is completely free.
* Mongo JavaScript editor, execute Mongo script (with NodeJs syntax)
* Redis tree view, generate script from keys, run Redis script
* Runs as application for Windows, Linux and Mac. Or in Docker container on server and in web Browser on client.
* Import, export from/to CSV, Excel, JSON, XML
* Import, export from/to CSV, Excel, JSON, NDJSON, XML
* 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)
* Archives - backup your data in NDJSON files on local filesystem (or on DbGate server, when using web application)
* Charts, export chart to HTML page
* For detailed info, how to run DbGate in docker container, visit [docker hub](https://hub.docker.com/r/dbgate/dbgate)
* Extensible plugin architecture
* Perspectives - nested table view over complex relational data, query designer on MongoDB databases
## How to contribute
Any contributions are welcome. If you want to contribute without coding, consider following:
* Tell your friends about DbGate or share on social networks - when more people will use DbGate, it will grow to be better
* Write review on [Slant.co](https://www.slant.co/improve/options/41086/~dbgate-review) or [G2](https://www.g2.com/products/dbgate/reviews)
* Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues.
* Become a backer on [Open collective](https://opencollective.com/dbgate)
* Where a small coding is acceptable for you, you could [create plugin](https://dbgate.org/docs/plugin-development.html). Plugins for new themes can be created actually without JS coding.
* Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues
* Create some tutorial video on [youtube](https://www.youtube.com/playlist?list=PLCo7KjCVXhr0RfUSjM9wJMsp_ShL1q61A)
* Become a backer on [GitHub sponsors](https://github.com/sponsors/dbgate) or [Open collective](https://opencollective.com/dbgate)
* Where a small coding is acceptable for you, you could [create plugin](https://dbgate.org/docs/plugin-development.html). Plugins for new themes can be created actually without JS coding
Thank you!
@@ -91,7 +94,7 @@ There are many database managers now, so why DbGate?
* Many data browsing functions based using foreign keys - master/detail, expand columns, expandable form view
## 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) .
* Application simplicity - DbGate takes the best and only the best from old DbGate, [DatAdmin](https://www.softpedia.com/get/Internet/Servers/Database-Utils/DatAdmin-Personal.shtml), [DbMouse](https://www.softpedia.com/get/Internet/Servers/Database-Utils/DbMouse.shtml) and [SQL Database Studio](https://en.wikipedia.org/wiki/SQL_Database_Studio)
* Minimal dependencies
* Frontend - Svelte
* Backend - NodeJs, ExpressJs, database connection drivers
@@ -172,4 +175,7 @@ cd dbgate-plugin-my-new-plugin # this directory is created by wizard, edit, what
yarn plugin # this compiles plugin and copies it into existing DbGate installation
```
After restarting DbGate, you could use your new plugin from DbGate.
After restarting DbGate, you could use your new plugin from DbGate.
## Logging
DbGate uses [pinomin logger](https://github.com/dbgate/pinomin). So by default, it produces JSON log messages into console and log files. If you want to see formatted logs, please use [pino-pretty](https://github.com/pinojs/pino-pretty) log formatter.

12
adjustPackageJson.js Normal file
View File

@@ -0,0 +1,12 @@
const fs = require('fs');
function adjustFile(file) {
const json = JSON.parse(fs.readFileSync(file, { encoding: 'utf-8' }));
if (process.platform != 'win32') {
delete json.optionalDependencies.msnodesqlv8;
}
fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8');
}
adjustFile('packages/api/package.json');
adjustFile('app/package.json');

View File

@@ -1,21 +1,674 @@
MIT License
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (c) 2021 Jan Prochazka
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
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:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
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.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,3 +1,7 @@
This project is licensed under the GPLv3 License. See the LICENSE file for full text of the GPLv3 license.
The original project was licensed under the MIT License, and the following notice applies to the original code:
MIT License
Copyright (c) 2021 Jan Prochazka
@@ -18,4 +22,4 @@ 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.
SOFTWARE.

BIN
app/icon512-mac.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.dbgate.DbGate</id>
<name>DbGate</name>
<summary>(no)SQL database client</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<developer_name>Jan Prochazka</developer_name>
<description>
<p>DbGate is cross-platform database manager. It's designed to be simple to use and effective, when working with more databases simultaneously. But there are also many advanced features like schema compare, visual query designer, chart visualisation or batch export and import.</p>
</description>
<url type="homepage">https://dbgate.org/</url>
<url type="vcs-browser">https://github.com/dbgate/dbgate</url>
<url type="contact">https://dbgate.org/about/</url>
<url type="donation">https://github.com/sponsors/dbgate</url>
<url type="bugtracker">https://github.com/dbgate/dbgate/issues</url>
<launchable type="desktop-id">org.dbgate.DbGate.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>
https://github.com/dbgate/dbgate/raw/c2abc83f994a56945c27fccea3df84b48005961f/img/screenshot1.png</image>
</screenshot>
<screenshot>
<image>
https://github.com/dbgate/dbgate/raw/c2abc83f994a56945c27fccea3df84b48005961f/img/screenshot2.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
<releases>
<release version="5.2.7" date="2024-05-13"/>
</releases>
</component>

View File

@@ -7,6 +7,7 @@
"dependencies": {
"electron-log": "^4.4.1",
"electron-updater": "^4.6.1",
"jsonwebtoken": "^9.0.2",
"lodash.clonedeepwith": "^4.5.0",
"patch-package": "^6.4.7"
},
@@ -19,9 +20,10 @@
"appId": "org.dbgate",
"productName": "DbGate",
"afterSign": "electron-builder-notarize",
"asarUnpack": "**/*.node",
"mac": {
"category": "database",
"icon": "icon512.png",
"icon": "icon512-mac.png",
"hardenedRuntime": true,
"entitlements": "entitlements.mac.plist",
"entitlementsInherit": "entitlements.mac.plist",
@@ -47,7 +49,8 @@
"armv7l",
"arm64"
]
}
},
"tar.gz"
],
"icon": "icons/",
"category": "Development",
@@ -71,7 +74,13 @@
},
"win": {
"target": [
"nsis",
{
"target": "nsis",
"arch": [
"x64",
"arm64"
]
},
{
"target": "zip",
"arch": [
@@ -107,12 +116,13 @@
"devDependencies": {
"copyfiles": "^2.2.0",
"cross-env": "^6.0.3",
"electron": "13.6.3",
"electron-builder": "22.14.5",
"electron-builder-notarize": "^1.4.0"
"electron": "30.0.2",
"electron-builder": "23.1.0",
"electron-builder-notarize": "^1.5.2"
},
"optionalDependencies": {
"better-sqlite3": "7.5.0",
"msnodesqlv8": "^2.4.4"
"better-sqlite3": "9.6.0",
"msnodesqlv8": "^4.2.1",
"oracledb": "^6.6.0"
}
}

View File

@@ -1,6 +1,8 @@
const electron = require('electron');
const os = require('os');
const fs = require('fs');
// const unhandled = require('electron-unhandled');
// const { openNewGitHubIssue, debugInfo } = require('electron-util');
const { Menu, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');
const log = require('electron-log');
@@ -14,7 +16,8 @@ const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const url = require('url');
const mainMenuDefinition = require('./mainMenuDefinition');
const { settings } = require('cluster');
const { isProApp, checkLicense } = require('./proTools');
let disableAutoUpgrade = false;
// require('@electron/remote/main').initialize();
@@ -22,16 +25,46 @@ const configRootPath = path.join(app.getPath('userData'), 'config-root.json');
let initialConfig = {};
let apiLoaded = false;
let mainModule;
// let getLogger;
// let loadLogsContent;
process.on('uncaughtException', function (error) {
console.error('uncaughtException', error);
});
const isMac = () => os.platform() == 'darwin';
// unhandled({
// showDialog: true,
// reportButton: error => {
// openNewGitHubIssue({
// user: 'dbgate',
// repo: 'dbgate',
// body: `PLEASE DELETE SENSITIVE INFO BEFORE POSTING ISSUE!!!\n\n\`\`\`\n${
// error.stack
// }\n\`\`\`\n\n---\n\n${debugInfo()}\n\n\`\`\`\n${loadLogsContent ? loadLogsContent(50) : ''}\n\`\`\``,
// });
// },
// logger: error => (getLogger ? getLogger('electron').fatal(error) : console.error(error)),
// });
try {
initialConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' }));
disableAutoUpgrade = initialConfig['disableAutoUpgrade'] || false;
} catch (err) {
console.log('Error loading config-root:', err.message);
initialConfig = {};
}
if (process.argv.includes('--disable-auto-upgrade')) {
console.log('Disabling auto-upgrade');
disableAutoUpgrade = true;
}
if (process.argv.includes('--enable-auto-upgrade')) {
console.log('Enabling auto-upgrade');
disableAutoUpgrade = false;
}
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
@@ -154,6 +187,10 @@ ipcMain.on('app-started', async (event, arg) => {
mainWindow.webContents.send('run-command', runCommandOnLoad);
runCommandOnLoad = null;
}
if (initialConfig['winIsMaximized']) {
mainWindow.webContents.send('setIsMaximized', true);
}
});
ipcMain.on('window-action', async (event, arg) => {
if (!mainWindow) {
@@ -164,11 +201,7 @@ ipcMain.on('window-action', async (event, arg) => {
mainWindow.minimize();
break;
case 'maximize':
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize();
break;
case 'close':
mainWindow.close();
@@ -211,6 +244,9 @@ ipcMain.on('window-action', async (event, arg) => {
case 'paste':
mainWindow.webContents.paste();
break;
case 'selectAll':
mainWindow.webContents.selectAll();
break;
}
});
@@ -240,10 +276,34 @@ function fillMissingSettings(value) {
return res;
}
function ensureBoundsVisible(bounds) {
const area = electron.screen.getDisplayMatching(bounds).workArea;
let { x, y, width, height } = bounds;
const isWithinDisplay =
x >= area.x && x + width <= area.x + area.width && y >= area.y && y + height <= area.y + area.height;
if (!isWithinDisplay) {
width = Math.min(width, area.width);
height = Math.min(height, area.height);
if (width < 400) width = 400;
if (height < 300) height = 300;
x = area.x; // + Math.round(area.width - width / 2);
y = area.y; // + Math.round(area.height - height / 2);
}
return { x, y, width, height };
}
function createWindow() {
const datadir = path.join(os.homedir(), '.dbgate');
let settingsJson = {};
let licenseKey = null;
try {
const datadir = path.join(os.homedir(), '.dbgate');
settingsJson = fillMissingSettings(
JSON.parse(fs.readFileSync(path.join(datadir, 'settings.json'), { encoding: 'utf-8' }))
);
@@ -251,9 +311,22 @@ function createWindow() {
console.log('Error loading settings.json:', err.message);
settingsJson = fillMissingSettings({});
}
if (isProApp()) {
try {
licenseKey = fs.readFileSync(path.join(datadir, 'license.key'), { encoding: 'utf-8' });
} catch (err) {
console.log('Error loading license.key:', err.message);
licenseKey = null;
}
}
const bounds = initialConfig['winBounds'];
useNativeMenu = settingsJson['app.useNativeMenu'];
const licenseOk = !isProApp() || checkLicense(licenseKey) == 'premium';
let bounds = initialConfig['winBounds'];
if (bounds) {
bounds = ensureBoundsVisible(bounds);
}
useNativeMenu = settingsJson['app.useNativeMenu'] || !licenseOk;
mainWindow = new BrowserWindow({
width: 1200,
@@ -296,6 +369,7 @@ function createWindow() {
JSON.stringify({
winBounds: mainWindow.getBounds(),
winIsMaximized: mainWindow.isMaximized(),
disableAutoUpgrade,
}),
'utf-8'
);
@@ -303,11 +377,26 @@ function createWindow() {
console.log('Error saving config-root:', err.message);
}
});
// mainWindow.webContents.toggleDevTools();
mainWindow.loadURL(startUrl);
if (os.platform() == 'linux') {
mainWindow.setIcon(path.resolve(__dirname, '../icon.png'));
}
// mainWindow.webContents.toggleDevTools();
mainWindow.on('maximize', () => {
mainWindow.webContents.send('setIsMaximized', true);
});
mainWindow.on('unmaximize', () => {
mainWindow.webContents.send('setIsMaximized', false);
});
// app.on('browser-window-focus', () => {
// const bounds = ensureBoundsVisible(mainWindow.getBounds());
// mainWindow.setBounds(bounds);
// });
}
if (!apiLoaded) {
@@ -327,9 +416,12 @@ function createWindow() {
// path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js')
// )
// );
api.configureLogger();
const main = api.getMainModule();
main.useAllControllers(null, electron);
mainModule = main;
// getLogger = api.getLogger;
// loadLogsContent = api.loadLogsContent;
apiLoaded = true;
}
mainModule.setElectronSender(mainWindow.webContents);
@@ -347,7 +439,10 @@ function createWindow() {
}
function onAppReady() {
if (!process.env.DEVMODE) {
if (disableAutoUpgrade) {
console.log('Auto-upgrade is disabled, run dbgate --enable-auto-upgrade to enable');
}
if (!process.env.DEVMODE && !disableAutoUpgrade) {
autoUpdater.checkForUpdatesAndNotify();
}
createWindow();

View File

@@ -6,6 +6,9 @@ module.exports = ({ editMenu }) => [
{ command: 'new.sqliteDatabase', hideDisabled: true },
{ divider: true },
{ command: 'new.query', hideDisabled: true },
{ command: 'new.queryDesign', hideDisabled: true },
{ command: 'new.diagram', hideDisabled: true },
{ command: 'new.perspective', hideDisabled: true },
{ command: 'new.freetable', hideDisabled: true },
{ command: 'new.shell', hideDisabled: true },
{ command: 'new.jsonl', hideDisabled: true },
@@ -18,6 +21,7 @@ module.exports = ({ editMenu }) => [
{ divider: true },
{ command: 'file.exit', hideDisabled: true },
{ command: 'app.logout', hideDisabled: true, skipInApp: true },
{ command: 'app.disconnect', hideDisabled: true, skipInApp: true },
],
},
{
@@ -44,6 +48,7 @@ module.exports = ({ editMenu }) => [
{ command: 'edit.cut' },
{ command: 'edit.copy' },
{ command: 'edit.paste' },
{ command: 'edit.selectAll' },
],
}
: null,
@@ -66,6 +71,7 @@ module.exports = ({ editMenu }) => [
{ command: 'app.toggleDevTools', hideDisabled: true },
{ command: 'app.toggleFullScreen', hideDisabled: true },
{ command: 'app.minimize', hideDisabled: true },
{ command: 'toggle.sidebar' },
{ divider: true },
{ command: 'theme.changeTheme', hideDisabled: true },
{ command: 'settings.show' },
@@ -81,6 +87,10 @@ module.exports = ({ editMenu }) => [
{ command: 'sql.generator', hideDisabled: true },
{ command: 'file.import', hideDisabled: true },
{ command: 'new.modelCompare', hideDisabled: true },
{ divider: true },
{ command: 'folder.showLogs', hideDisabled: true },
{ command: 'folder.showData', hideDisabled: true },
{ command: 'new.gist', hideDisabled: true },
],
},
{

12
app/src/proTools.js Normal file
View File

@@ -0,0 +1,12 @@
function isProApp() {
return false;
}
function checkLicense(license) {
return null;
}
module.exports = {
isProApp,
checkLicense,
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,21 @@
FROM node:14
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
curl \
gnupg \
iputils-ping \
iproute2 \
&& rm -rf /var/lib/apt/lists/*
unixodbc \
gcc \
g++ \
make
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_18.x jammy main" | tee /etc/apt/sources.list.d/nodesource.list \
&& echo "deb-src [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_18.x jammy main" | tee -a /etc/apt/sources.list.d/nodesource.list \
&& apt-get update && apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g yarn
WORKDIR /home/dbgate-docker

View File

@@ -1,4 +1,4 @@
FROM node:14-alpine
FROM node:18-alpine
WORKDIR /home/dbgate-docker

View File

@@ -8,4 +8,4 @@ then
echo "$HOST_IP $HOST_DOMAIN" >> /etc/hosts
fi
node bundle.js --listen-api
exec node bundle.js --listen-api

View File

@@ -3,11 +3,12 @@ const fs = require('fs');
let fillContent = '';
if (process.platform == 'win32') {
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');`;
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');\n`;
}
fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');`;
fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');\n`;
fillContent += `content['oracledb'] = () => require('oracledb');\n`;
const getContent = (empty) => `
const getContent = empty => `
// this file is generated automatically by script fillNativeModules.js, do not edit it manually
const content = {};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -1,7 +1,6 @@
const stableStringify = require('json-stable-stringify');
const _ = require('lodash');
const fp = require('lodash/fp');
const uuidv1 = require('uuid/v1');
const { testWrapper } = require('../tools');
const engines = require('../engines');
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');

View File

@@ -1,9 +1,9 @@
const stableStringify = require('json-stable-stringify');
const _ = require('lodash');
const fp = require('lodash/fp');
const uuidv1 = require('uuid/v1');
const { testWrapper } = require('../tools');
const engines = require('../engines');
const crypto = require('crypto');
const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
function pickImportantTableInfo(table) {
@@ -76,7 +76,7 @@ describe('Alter table', () => {
tbl.columns.push({
columnName: 'added',
dataType: 'int',
pairingId: uuidv1(),
pairingId: crypto.randomUUID(),
notNull: false,
autoIncrement: false,
});

View File

@@ -0,0 +1,94 @@
const engines = require('../engines');
const stream = require('stream');
const { testWrapper } = require('../tools');
const dataDuplicator = require('dbgate-api/src/shell/dataDuplicator');
const { runCommandOnDriver } = require('dbgate-tools');
describe('Data duplicator', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Insert simple data - %s',
testWrapper(async (conn, driver, engine) => {
runCommandOnDriver(conn, driver, dmp =>
dmp.createTable({
pureName: 't1',
columns: [
{ columnName: 'id', dataType: 'int', autoIncrement: true, notNull: true },
{ columnName: 'val', dataType: 'varchar(50)' },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
})
);
runCommandOnDriver(conn, driver, dmp =>
dmp.createTable({
pureName: 't2',
columns: [
{ columnName: 'id', dataType: 'int', autoIncrement: true, notNull: true },
{ columnName: 'val', dataType: 'varchar(50)' },
{ columnName: 'valfk', dataType: 'int', notNull: true },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
foreignKeys: [{ refTableName: 't1', columns: [{ columnName: 'valfk', refColumnName: 'id' }] }],
})
);
const gett1 = () =>
stream.Readable.from([
{ __isStreamHeader: true, __isDynamicStructure: true },
{ id: 1, val: 'v1' },
{ id: 2, val: 'v2' },
{ id: 3, val: 'v3' },
]);
const gett2 = () =>
stream.Readable.from([
{ __isStreamHeader: true, __isDynamicStructure: true },
{ id: 1, val: 'v1', valfk: 1 },
{ id: 2, val: 'v2', valfk: 2 },
{ id: 3, val: 'v3', valfk: 3 },
]);
await dataDuplicator({
systemConnection: conn,
driver,
items: [
{
name: 't1',
operation: 'copy',
openStream: gett1,
},
{
name: 't2',
operation: 'copy',
openStream: gett2,
},
],
});
await dataDuplicator({
systemConnection: conn,
driver,
items: [
{
name: 't1',
operation: 'copy',
openStream: gett1,
},
{
name: 't2',
operation: 'copy',
openStream: gett2,
},
],
});
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
expect(res1.rows[0].cnt.toString()).toEqual('6');
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
expect(res2.rows[0].cnt.toString()).toEqual('6');
})
);
});

View File

@@ -136,8 +136,8 @@ const filterLocal = [
'-MySQL',
'-MariaDB',
'-PostgreSQL',
'SQL Server',
'-SQLite',
'-SQL Server',
'SQLite',
'-CockroachDB',
];

View File

@@ -7,12 +7,14 @@
"url": "https://github.com/dbgate/dbgate.git"
},
"author": "Jan Prochazka",
"license": "MIT",
"license": "GPL-3.0",
"scripts": {
"wait:local": "cross-env DEVMODE=1 LOCALTEST=1 node wait.js",
"wait:ci": "cross-env DEVMODE=1 CITEST=1 node wait.js",
"test:local": "cross-env DEVMODE=1 LOCALTEST=1 jest",
"test:local:path": "cross-env DEVMODE=1 LOCALTEST=1 jest --runTestsByPath __tests__/data-duplicator.spec.js",
"test:ci": "cross-env DEVMODE=1 CITEST=1 jest --runInBand --json --outputFile=result.json --testLocationInResults",
"run:local": "docker-compose down && docker-compose up -d && yarn wait:local && yarn test:local"

View File

@@ -1,4 +1,8 @@
global.DBGATE_TOOLS = require('dbgate-tools');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
};
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
const crypto = require('crypto');
@@ -44,16 +48,18 @@ async function connect(engine, database) {
}
}
const testWrapper = body => async (label, ...other) => {
const engine = other[other.length - 1];
const driver = requireEngineDriver(engine.connection);
const conn = await connect(engine, randomDbName());
try {
await body(conn, driver, ...other);
} finally {
await driver.close(conn);
}
};
const testWrapper =
body =>
async (label, ...other) => {
const engine = other[other.length - 1];
const driver = requireEngineDriver(engine.connection);
const conn = await connect(engine, randomDbName());
try {
await body(conn, driver, ...other);
} finally {
await driver.close(conn);
}
};
module.exports = {
randomDbName,

View File

@@ -1,7 +1,10 @@
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
const engines = require('./engines');
const { extractConnection } = require('./tools');
global.DBGATE_TOOLS = require('dbgate-tools');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
};
async function connectEngine(engine) {
const connection = extractConnection(engine);

View File

@@ -104,3 +104,6 @@ magick icon.png -resize 16x16! ../app/icons/16x16.png
magick icon.png -resize 192x192! ../packages/web/public/logo192.png
magick icon.png -resize 512x512! ../packages/web/public/logo512.png
magick icon.png -define icon:auto-resize="256,128,96,64,48,32,16" ../packages/web/public/favicon.ico
convert icon.png -resize 800x800 -background transparent -gravity center -extent 1000x1000 iconmac.png
magick composite iconmac.png macbg.png -resize 600x600! ../app/icon512-mac.png

BIN
misc/iconmac.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

BIN
misc/macbg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

14
misc/play-dark-mode.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 17.804 17.804" style="enable-background:new 0 0 17.804 17.804;" xml:space="preserve">
<g>
<g id="c98_play">
<path fill='#ccc' d="M2.067,0.043C2.21-0.028,2.372-0.008,2.493,0.085l13.312,8.503c0.094,0.078,0.154,0.191,0.154,0.313
c0,0.12-0.061,0.237-0.154,0.314L2.492,17.717c-0.07,0.057-0.162,0.087-0.25,0.087l-0.176-0.04
c-0.136-0.065-0.222-0.207-0.222-0.361V0.402C1.844,0.25,1.93,0.107,2.067,0.043z"/>
</g>
<g id="Capa_1_78_">
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 733 B

14
misc/play-light-mode.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 17.804 17.804" style="enable-background:new 0 0 17.804 17.804;" xml:space="preserve">
<g>
<g id="c98_play">
<path fill='#444' d="M2.067,0.043C2.21-0.028,2.372-0.008,2.493,0.085l13.312,8.503c0.094,0.078,0.154,0.191,0.154,0.313
c0,0.12-0.061,0.237-0.154,0.314L2.492,17.717c-0.07,0.057-0.162,0.087-0.25,0.087l-0.176-0.04
c-0.136-0.065-0.222-0.207-0.222-0.361V0.402C1.844,0.25,1.93,0.107,2.067,0.043z"/>
</g>
<g id="Capa_1_78_">
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View File

@@ -1,6 +1,6 @@
{
"private": true,
"version": "5.0.8-beta.3",
"version": "5.3.5-beta.6",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -8,14 +8,20 @@
"integration-tests"
],
"scripts": {
"start:api": "yarn workspace dbgate-api start",
"start:app": "cd app && yarn start",
"start:api": "yarn workspace dbgate-api start | pino-pretty",
"start:api:json": "yarn workspace dbgate-api start",
"start:app": "cd app && yarn start | pino-pretty",
"start:app:singledb": "CONNECTIONS=con1 SERVER_con1=localhost ENGINE_con1=mysql@dbgate-plugin-mysql USER_con1=root PASSWORD_con1=Pwd2020Db SINGLE_CONNECTION=con1 SINGLE_DATABASE=Chinook yarn start:app",
"start:api:debug": "cross-env DEBUG=* yarn workspace dbgate-api start",
"start:app:debug": "cd app && cross-env DEBUG=* yarn start",
"start:api:debug:ssh": "cross-env DEBUG=ssh yarn workspace dbgate-api start",
"start:app:debug:ssh": "cd app && cross-env DEBUG=ssh yarn start",
"start:api:portal": "yarn workspace dbgate-api start:portal",
"start:api:singledb": "yarn workspace dbgate-api start:singledb",
"start:api:portal": "yarn workspace dbgate-api start:portal | pino-pretty",
"start:api:singledb": "yarn workspace dbgate-api start:singledb | pino-pretty",
"start:api:auth": "yarn workspace dbgate-api start:auth | pino-pretty",
"start:api:dblogin": "yarn workspace dbgate-api start:dblogin | pino-pretty",
"start:api:storage": "yarn workspace dbgate-api start:storage | pino-pretty",
"sync:pro": "cd sync && yarn start",
"start:web": "yarn workspace dbgate-web dev",
"start:sqltree": "yarn workspace dbgate-sqltree start",
"start:tools": "yarn workspace dbgate-tools start",
@@ -31,19 +37,22 @@
"build:web:docker": "yarn workspace dbgate-web build",
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
"build:plugins:frontend:watch": "workspaces-run --parallel --only=\"dbgate-plugin-*\" -- yarn build:frontend:watch",
"storage-json": "dbmodel model-to-json storage-db packages/api/src/storageModel.js --commonjs",
"plugins:copydist": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn copydist",
"build:app:local": "yarn plugins:copydist && cd app && yarn build:local",
"start:app:local": "cd app && yarn start:local",
"setCurrentVersion": "node setCurrentVersion",
"printSecrets": "node printSecrets",
"generatePadFile": "node generatePadFile",
"adjustPackageJson": "node adjustPackageJson",
"fillNativeModules": "node fillNativeModules",
"fillNativeModulesElectron": "node fillNativeModules --electron",
"fillPackagedPlugins": "node fillPackagedPlugins",
"resetPackagedPlugins": "node resetPackagedPlugins",
"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 && copyfiles \"plugins/dist/**/*\" docker/plugins -u 2",
"install:sqlite:docker": "cd docker && yarn init --yes && yarn add better-sqlite3 && cd ..",
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build && yarn install:sqlite:docker",
"install:drivers:docker": "cd docker && yarn init --yes && yarn add better-sqlite3 && yarn add oracledb && cd ..",
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build && yarn install:drivers:docker",
"start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"",
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn build:plugins:frontend:watch\"",
"ts:api": "yarn workspace dbgate-api ts",
@@ -54,7 +63,8 @@
},
"dependencies": {
"concurrently": "^5.1.0",
"patch-package": "^6.2.1"
"patch-package": "^6.2.1",
"pino-pretty": "^9.1.1"
},
"devDependencies": {
"copyfiles": "^2.2.0",

1
packages/api/env/auth/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

14
packages/api/env/dblogin/.env vendored Normal file
View File

@@ -0,0 +1,14 @@
DEVMODE=1
CONNECTIONS=mysql
SINGLE_CONNECTION=mysql
# SINGLE_DATABASE=Chinook
LABEL_mysql=MySql localhost
SERVER_mysql=localhost
# USER_mysql=root
PORT_mysql=3306
# PASSWORD_mysql=Pwd2020Db
ENGINE_mysql=mysql@dbgate-plugin-mysql
# PASSWORD_MODE_mysql=askPassword
PASSWORD_MODE_mysql=askUser

View File

@@ -1,6 +1,6 @@
DEVMODE=1
CONNECTIONS=mysql,postgres,mongo,mongo2,mysqlssh,sqlite,relational
CONNECTIONS=mysql,postgres,postgres1,mongo,mongo2,mysqlssh,sqlite,relational
LABEL_mysql=MySql localhost
SERVER_mysql=localhost
@@ -12,10 +12,18 @@ ENGINE_mysql=mysql@dbgate-plugin-mysql
LABEL_postgres=Postgres localhost
SERVER_postgres=localhost
USER_postgres=postgres
PASSWORD_postgres=test
PORT_postgres=5433
PASSWORD_postgres=Pwd2020Db
PORT_postgres=5432
ENGINE_postgres=postgres@dbgate-plugin-postgres
LABEL_postgres1=Postgres localhost test DB
SERVER_postgres1=localhost
USER_postgres1=postgres
PASSWORD_postgres1=Pwd2020Db
PORT_postgres1=5432
ENGINE_postgres1=postgres@dbgate-plugin-postgres
DATABASE_postgres1=test
LABEL_mongo=Mongo URL
URL_mongo=mongodb://localhost:27017
ENGINE_mongo=mongo@dbgate-plugin-mongo

View File

@@ -5,8 +5,8 @@ CONNECTIONS=mysql
LABEL_mysql=MySql localhost
SERVER_mysql=localhost
USER_mysql=root
PASSWORD_mysql=test
PORT_mysql=3307
PASSWORD_mysql=Pwd2020Db
PORT_mysql=3306
ENGINE_mysql=mysql@dbgate-plugin-mysql
DBCONFIG_mysql=[{"name":"Chinook","connectionColor":"cyan"}]

View File

@@ -8,7 +8,7 @@
"url": "https://github.com/dbgate/dbgate.git"
},
"author": "Jan Prochazka",
"license": "MIT",
"license": "GPL-3.0",
"keywords": [
"sql",
"json",
@@ -17,6 +17,7 @@
"dbgate"
],
"dependencies": {
"activedirectory2": "^2.1.0",
"async-lock": "^1.2.4",
"axios": "^0.21.1",
"body-parser": "^1.19.0",
@@ -25,7 +26,8 @@
"compare-versions": "^3.6.0",
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-query-splitter": "^4.9.0",
"dbgate-datalib": "^5.0.0-alpha.1",
"dbgate-query-splitter": "^4.10.1",
"dbgate-sqltree": "^5.0.0-alpha.1",
"dbgate-tools": "^5.0.0-alpha.1",
"debug": "^4.3.4",
@@ -35,6 +37,7 @@
"express": "^4.17.1",
"express-basic-auth": "^1.2.0",
"express-fileupload": "^1.2.0",
"external-sorting": "^1.3.1",
"fs-extra": "^9.1.0",
"fs-reverse": "^0.0.3",
"get-port": "^5.1.1",
@@ -42,21 +45,28 @@
"is-electron": "^2.2.1",
"js-yaml": "^4.1.0",
"json-stable-stringify": "^1.0.1",
"jsonwebtoken": "^8.5.1",
"line-reader": "^0.4.0",
"lodash": "^4.17.21",
"moment": "^2.24.0",
"ncp": "^2.0.0",
"node-cron": "^2.0.3",
"on-finished": "^2.4.1",
"pinomin": "^1.0.4",
"portfinder": "^1.0.28",
"rimraf": "^3.0.0",
"simple-encryptor": "^4.0.0",
"ssh2": "^1.11.0",
"tar": "^6.0.5",
"uuid": "^3.4.0"
"tar": "^6.0.5"
},
"scripts": {
"start": "env-cmd node src/index.js --listen-api",
"start": "env-cmd -f .env node src/index.js --listen-api",
"start:portal": "env-cmd -f env/portal/.env node src/index.js --listen-api",
"start:singledb": "env-cmd -f env/singledb/.env node src/index.js --listen-api",
"start:auth": "env-cmd -f env/auth/.env node src/index.js --listen-api",
"start:dblogin": "env-cmd -f env/dblogin/.env node src/index.js --listen-api",
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db --listen-api",
"start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
"ts": "tsc",
"build": "webpack"
@@ -69,11 +79,12 @@
"node-loader": "^1.0.2",
"nodemon": "^2.0.2",
"typescript": "^4.4.3",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4"
},
"optionalDependencies": {
"better-sqlite3": "7.5.0",
"msnodesqlv8": "^2.4.4"
"better-sqlite3": "9.6.0",
"msnodesqlv8": "^4.2.1",
"oracledb": "^6.6.0"
}
}

View File

@@ -0,0 +1,16 @@
const crypto = require('crypto');
const tokenSecret = crypto.randomUUID();
function getTokenLifetime() {
return process.env.TOKEN_LIFETIME || '1d';
}
function getTokenSecret() {
return tokenSecret;
}
module.exports = {
getTokenLifetime,
getTokenSecret,
};

View File

@@ -0,0 +1,322 @@
const { getTokenSecret, getTokenLifetime } = require('./authCommon');
const _ = require('lodash');
const axios = require('axios');
const { getLogger, getPredefinedPermissions } = require('dbgate-tools');
const AD = require('activedirectory2').promiseWrapper;
const jwt = require('jsonwebtoken');
const logger = getLogger('authProvider');
class AuthProviderBase {
amoid = 'none';
async login(login, password, options = undefined) {
return {
accessToken: jwt.sign(
{
amoid: this.amoid,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
};
}
oauthToken(params) {
return {};
}
getCurrentLogin(req) {
const login = req?.user?.login ?? req?.auth?.user ?? null;
return login;
}
isUserLoggedIn(req) {
return !!req?.user || !!req?.auth;
}
getCurrentPermissions(req) {
const login = this.getCurrentLogin(req);
const permissions = process.env[`LOGIN_PERMISSIONS_${login}`];
return permissions || process.env.PERMISSIONS;
}
getLoginPageConnections() {
return null;
}
getSingleConnectionId(req) {
return null;
}
toJson() {
return {
amoid: this.amoid,
workflowType: 'anonymous',
name: 'Anonymous',
};
}
async redirect({ state }) {
return {
status: 'error',
};
}
async getLogoutUrl() {
return null;
}
}
class OAuthProvider extends AuthProviderBase {
amoid = 'oauth';
async oauthToken(params) {
const { redirectUri, code } = params;
const scopeParam = process.env.OAUTH_SCOPE ? `&scope=${process.env.OAUTH_SCOPE}` : '';
const resp = await axios.default.post(
`${process.env.OAUTH_TOKEN}`,
`grant_type=authorization_code&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(
redirectUri
)}&client_id=${process.env.OAUTH_CLIENT_ID}&client_secret=${process.env.OAUTH_CLIENT_SECRET}${scopeParam}`
);
const { access_token, refresh_token } = resp.data;
const payload = jwt.decode(access_token);
logger.info({ payload }, 'User payload returned from OAUTH');
const login =
process.env.OAUTH_LOGIN_FIELD && payload && payload[process.env.OAUTH_LOGIN_FIELD]
? payload[process.env.OAUTH_LOGIN_FIELD]
: 'oauth';
if (
process.env.OAUTH_ALLOWED_LOGINS &&
!process.env.OAUTH_ALLOWED_LOGINS.split(',').find(x => x.toLowerCase().trim() == login.toLowerCase().trim())
) {
return { error: `Username ${login} not allowed to log in` };
}
const groups =
process.env.OAUTH_GROUP_FIELD && payload && payload[process.env.OAUTH_GROUP_FIELD]
? payload[process.env.OAUTH_GROUP_FIELD]
: [];
const allowedGroups = process.env.OAUTH_ALLOWED_GROUPS
? process.env.OAUTH_ALLOWED_GROUPS.split(',').map(group => group.toLowerCase().trim())
: [];
if (process.env.OAUTH_ALLOWED_GROUPS && !groups.some(group => allowedGroups.includes(group.toLowerCase().trim()))) {
return { error: `Username ${login} does not belong to an allowed group` };
}
if (access_token) {
return {
accessToken: jwt.sign({ login }, getTokenSecret(), { expiresIn: getTokenLifetime() }),
};
}
return { error: 'Token not found' };
}
async getLogoutUrl() {
return process.env.OAUTH_LOGOUT;
}
toJson() {
return {
...super.toJson(),
workflowType: 'redirect',
name: 'OAuth 2.0',
};
}
redirect({ state, redirectUri }) {
const scopeParam = process.env.OAUTH_SCOPE ? `&scope=${process.env.OAUTH_SCOPE}` : '';
return {
status: 'ok',
uri: `${process.env.OAUTH_AUTH}?client_id=${
process.env.OAUTH_CLIENT_ID
}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(
state
)}${scopeParam}`,
};
}
}
class ADProvider extends AuthProviderBase {
amoid = 'ad';
async login(login, password, options = undefined) {
const adConfig = {
url: process.env.AD_URL,
baseDN: process.env.AD_BASEDN,
username: process.env.AD_USERNAME,
password: process.env.AD_PASSWORD,
};
const ad = new AD(adConfig);
try {
const res = await ad.authenticate(login, password);
if (!res) {
return { error: 'Login failed' };
}
if (
process.env.AD_ALLOWED_LOGINS &&
!process.env.AD_ALLOWED_LOGINS.split(',').find(x => x.toLowerCase().trim() == login.toLowerCase().trim())
) {
return { error: `Username ${login} not allowed to log in` };
}
return {
accessToken: jwt.sign(
{
amoid: this.amoid,
login,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
};
} catch (e) {
return { error: 'Login failed' };
}
}
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
name: 'Active Directory',
};
}
}
class LoginsProvider extends AuthProviderBase {
amoid = 'logins';
async login(login, password, options = undefined) {
if (password == process.env[`LOGIN_PASSWORD_${login}`]) {
return {
accessToken: jwt.sign(
{
amoid: this.amoid,
login,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
};
}
return { error: 'Invalid credentials' };
}
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
name: 'Login & Password',
};
}
}
class DenyAllProvider extends AuthProviderBase {
amoid = 'deny';
async login(login, password, options = undefined) {
return { error: 'Login not allowed' };
}
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
name: 'Deny all',
};
}
}
function hasEnvLogins() {
if (process.env.LOGIN && process.env.PASSWORD) {
return true;
}
for (const key in process.env) {
if (key.startsWith('LOGIN_PASSWORD_')) {
return true;
}
}
return false;
}
function detectEnvAuthProvider() {
if (process.env.AUTH_PROVIDER) {
return process.env.AUTH_PROVIDER;
}
if (process.env.STORAGE_DATABASE) {
return 'denyall';
}
if (process.env.OAUTH_AUTH) {
return 'oauth';
}
if (process.env.AD_URL) {
return 'ad';
}
if (hasEnvLogins()) {
return 'logins';
}
return 'none';
}
function createEnvAuthProvider() {
const authProvider = detectEnvAuthProvider();
switch (authProvider) {
case 'oauth':
return new OAuthProvider();
case 'ad':
return new ADProvider();
case 'logins':
return new LoginsProvider();
case 'denyall':
return new DenyAllProvider();
default:
return new AuthProviderBase();
}
}
let defaultAuthProvider = createEnvAuthProvider();
let authProviders = [defaultAuthProvider];
function getAuthProviders() {
return authProviders;
}
function getAuthProviderById(amoid) {
return authProviders.find(x => x.amoid == amoid);
}
function getDefaultAuthProvider() {
return defaultAuthProvider;
}
function getAuthProviderFromReq(req) {
const authProviderId = req?.auth?.amoid || req?.user?.amoid;
return getAuthProviderById(authProviderId) ?? getDefaultAuthProvider();
}
function setAuthProviders(value, defaultProvider = null) {
authProviders = value;
defaultAuthProvider = defaultProvider || value[0];
}
module.exports = {
AuthProviderBase,
detectEnvAuthProvider,
getAuthProviders,
getDefaultAuthProvider,
setAuthProviders,
getAuthProviderById,
getAuthProviderFromReq,
};

View File

@@ -58,7 +58,7 @@ module.exports = {
refreshFiles_meta: true,
async refreshFiles({ folder }) {
socket.emitChanged(`app-files-changed-${folder}`);
socket.emitChanged('app-files-changed', { app: folder });
},
refreshFolders_meta: true,
@@ -69,7 +69,7 @@ module.exports = {
deleteFile_meta: true,
async deleteFile({ folder, file, fileType }) {
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
socket.emitChanged(`app-files-changed-${folder}`);
socket.emitChanged('app-files-changed', { app: folder });
this.emitChangedDbApp(folder);
},
@@ -79,7 +79,7 @@ module.exports = {
path.join(path.join(appdir(), folder), `${file}.${fileType}`),
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
);
socket.emitChanged(`app-files-changed-${folder}`);
socket.emitChanged('app-files-changed', { app: folder });
this.emitChangedDbApp(folder);
},
@@ -95,7 +95,7 @@ module.exports = {
if (!folder) throw new Error('Missing folder parameter');
await fs.rmdir(path.join(appdir(), folder), { recursive: true });
socket.emitChanged(`app-folders-changed`);
socket.emitChanged(`app-files-changed-${folder}`);
socket.emitChanged('app-files-changed', { app: folder });
socket.emitChanged('used-apps-changed');
},
@@ -219,7 +219,7 @@ module.exports = {
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
socket.emitChanged(`app-files-changed-${appFolder}`);
socket.emitChanged('app-files-changed', { app: appFolder });
socket.emitChanged('used-apps-changed');
},
@@ -271,7 +271,7 @@ module.exports = {
const file = path.join(appdir(), appFolder, fileName);
if (!(await fs.exists(file))) {
await fs.writeFile(file, JSON.stringify(content, undefined, 2));
socket.emitChanged(`app-files-changed-${appFolder}`);
socket.emitChanged('app-files-changed', { app: appFolder });
socket.emitChanged('used-apps-changed');
return true;
}

View File

@@ -1,10 +1,17 @@
const fs = require('fs-extra');
const readline = require('readline');
const crypto = require('crypto');
const path = require('path');
const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('../utility/directories');
const socket = require('../utility/socket');
const { saveFreeTableData } = require('../utility/freeTableStorage');
const loadFilesRecursive = require('../utility/loadFilesRecursive');
const getJslFileName = require('../utility/getJslFileName');
const { getLogger } = require('dbgate-tools');
const dbgateApi = require('../shell');
const jsldata = require('./jsldata');
const platformInfo = require('../utility/platformInfo');
const logger = getLogger('archive');
module.exports = {
folders_meta: true,
@@ -67,25 +74,28 @@ module.exports = {
...fileType('.matview.sql', 'matview.sql'),
];
} catch (err) {
console.log('Error reading archive files', err.message);
logger.error({ err }, 'Error reading archive files');
return [];
}
},
refreshFiles_meta: true,
async refreshFiles({ folder }) {
socket.emitChanged(`archive-files-changed-${folder}`);
socket.emitChanged('archive-files-changed', { folder });
return true;
},
refreshFolders_meta: true,
async refreshFolders() {
socket.emitChanged(`archive-folders-changed`);
return true;
},
deleteFile_meta: true,
async deleteFile({ folder, file, fileType }) {
await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.${fileType}`));
socket.emitChanged(`archive-files-changed-${folder}`);
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},
renameFile_meta: true,
@@ -94,7 +104,47 @@ module.exports = {
path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
path.join(resolveArchiveFolder(folder), `${newFile}.${fileType}`)
);
socket.emitChanged(`archive-files-changed-${folder}`);
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},
modifyFile_meta: true,
async modifyFile({ folder, file, changeSet, mergedRows, mergeKey, mergeMode }) {
await jsldata.closeDataStore(`archive://${folder}/${file}`);
const changedFilePath = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
if (!fs.existsSync(changedFilePath)) {
if (!mergedRows) {
return false;
}
const fileStream = fs.createWriteStream(changedFilePath);
for (const row of mergedRows) {
await fileStream.write(JSON.stringify(row) + '\n');
}
await fileStream.close();
socket.emitChanged(`archive-files-changed`, { folder });
return true;
}
const tmpchangedFilePath = path.join(resolveArchiveFolder(folder), `${file}-${crypto.randomUUID()}.jsonl`);
const reader = await dbgateApi.modifyJsonLinesReader({
fileName: changedFilePath,
changeSet,
mergedRows,
mergeKey,
mergeMode,
});
const writer = await dbgateApi.jsonLinesWriter({ fileName: tmpchangedFilePath });
await dbgateApi.copyStream(reader, writer);
if (platformInfo.isWindows) {
await fs.copyFile(tmpchangedFilePath, changedFilePath);
await fs.unlink(tmpchangedFilePath);
} else {
await fs.unlink(changedFilePath);
await fs.rename(tmpchangedFilePath, changedFilePath);
}
return true;
},
renameFolder_meta: true,
@@ -102,6 +152,7 @@ module.exports = {
const uniqueName = await this.getNewArchiveFolder({ database: newFolder });
await fs.rename(path.join(archivedir(), folder), path.join(archivedir(), uniqueName));
socket.emitChanged(`archive-folders-changed`);
return true;
},
deleteFolder_meta: true,
@@ -113,40 +164,42 @@ module.exports = {
await fs.rmdir(path.join(archivedir(), folder), { recursive: true });
}
socket.emitChanged(`archive-folders-changed`);
},
saveFreeTable_meta: true,
async saveFreeTable({ folder, file, data }) {
await saveFreeTableData(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), data);
socket.emitChanged(`archive-files-changed-${folder}`);
return true;
},
loadFreeTable_meta: true,
async loadFreeTable({ folder, file }) {
return new Promise((resolve, reject) => {
const fileStream = fs.createReadStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
const liner = readline.createInterface({
input: fileStream,
});
let structure = null;
const rows = [];
liner.on('line', line => {
const data = JSON.parse(line);
if (structure) rows.push(data);
else structure = data;
});
liner.on('close', () => {
resolve({ structure, rows });
fileStream.close();
});
});
},
saveText_meta: true,
async saveText({ folder, file, text }) {
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
socket.emitChanged(`archive-files-changed-${folder}`);
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},
saveJslData_meta: true,
async saveJslData({ folder, file, jslid, changeSet }) {
const source = getJslFileName(jslid);
const target = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
if (changeSet) {
const reader = await dbgateApi.modifyJsonLinesReader({
fileName: source,
changeSet,
});
const writer = await dbgateApi.jsonLinesWriter({ fileName: target });
await dbgateApi.copyStream(reader, writer);
} else {
await fs.copyFile(source, target);
socket.emitChanged(`archive-files-changed`, { folder });
}
return true;
},
saveRows_meta: true,
async saveRows({ folder, file, rows }) {
const fileStream = fs.createWriteStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
for (const row of rows) {
await fileStream.write(JSON.stringify(row) + '\n');
}
await fileStream.close();
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},

View File

@@ -0,0 +1,128 @@
const axios = require('axios');
const jwt = require('jsonwebtoken');
const getExpressPath = require('../utility/getExpressPath');
const { getLogger } = require('dbgate-tools');
const AD = require('activedirectory2').promiseWrapper;
const crypto = require('crypto');
const { getTokenSecret, getTokenLifetime } = require('../auth/authCommon');
const {
getAuthProviderFromReq,
getAuthProviders,
getDefaultAuthProvider,
getAuthProviderById,
} = require('../auth/authProvider');
const storage = require('./storage');
const logger = getLogger('auth');
function unauthorizedResponse(req, res, text) {
// if (req.path == getExpressPath('/config/get-settings')) {
// return res.json({});
// }
// if (req.path == getExpressPath('/connections/list')) {
// return res.json([]);
// }
return res.sendStatus(401).send(text);
}
function authMiddleware(req, res, next) {
const SKIP_AUTH_PATHS = [
'/config/get',
'/config/logout',
'/config/get-settings',
'/config/save-license-key',
'/auth/oauth-token',
'/auth/login',
'/auth/redirect',
'/stream',
'storage/get-connections-for-login-page',
'auth/get-providers',
'/connections/dblogin-web',
'/connections/dblogin-app',
'/connections/dblogin-auth',
'/connections/dblogin-auth-token',
];
// console.log('********************* getAuthProvider()', getAuthProvider());
// const isAdminPage = req.headers['x-is-admin-page'] == 'true';
if (process.env.BASIC_AUTH) {
// API is not authorized for basic auth
return next();
}
let skipAuth = !!SKIP_AUTH_PATHS.find(x => req.path == getExpressPath(x));
const authHeader = req.headers.authorization;
if (!authHeader) {
if (skipAuth) {
return next();
}
return unauthorizedResponse(req, res, 'missing authorization header');
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, getTokenSecret());
req.user = decoded;
return next();
} catch (err) {
if (skipAuth) {
return next();
}
logger.error({ err }, 'Sending invalid token error');
return unauthorizedResponse(req, res, 'invalid token');
}
}
module.exports = {
oauthToken_meta: true,
async oauthToken(params) {
const { amoid } = params;
return getAuthProviderById(amoid).oauthToken(params);
},
login_meta: true,
async login(params) {
const { amoid, login, password, isAdminPage } = params;
if (isAdminPage) {
if (process.env.ADMIN_PASSWORD && process.env.ADMIN_PASSWORD == password) {
return {
accessToken: jwt.sign(
{
login: 'superadmin',
permissions: await storage.loadSuperadminPermissions(),
roleId: -3,
},
getTokenSecret(),
{
expiresIn: getTokenLifetime(),
}
),
};
}
return { error: 'Login failed' };
}
return getAuthProviderById(amoid).login(login, password);
},
getProviders_meta: true,
getProviders() {
return {
providers: getAuthProviders().map(x => x.toJson()),
default: getDefaultAuthProvider()?.amoid,
};
},
redirect_meta: true,
async redirect(params) {
const { amoid } = params;
return getAuthProviderById(amoid).redirect(params);
},
authMiddleware,
};

View File

@@ -2,8 +2,8 @@ const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const axios = require('axios');
const { datadir } = require('../utility/directories');
const { hasPermission, getLogins } = require('../utility/hasPermission');
const { datadir, getLogsFilePath } = require('../utility/directories');
const { hasPermission } = require('../utility/hasPermission');
const socket = require('../utility/socket');
const _ = require('lodash');
const AsyncLock = require('async-lock');
@@ -11,6 +11,9 @@ const AsyncLock = require('async-lock');
const currentVersion = require('../currentVersion');
const platformInfo = require('../utility/platformInfo');
const connections = require('../controllers/connections');
const { getAuthProviderFromReq } = require('../auth/authProvider');
const { checkLicense, checkLicenseKey } = require('../utility/checkLicense');
const storage = require('./storage');
const lock = new AsyncLock();
@@ -27,19 +30,53 @@ module.exports = {
get_meta: true,
async get(_params, req) {
const logins = getLogins();
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
const permissions = login ? login.permissions : process.env.PERMISSIONS;
const authProvider = getAuthProviderFromReq(req);
const login = authProvider.getCurrentLogin(req);
const permissions = authProvider.getCurrentPermissions(req);
const isUserLoggedIn = authProvider.isUserLoggedIn(req);
const singleConid = authProvider.getSingleConnectionId(req);
const singleConnection = singleConid
? await connections.getCore({ conid: singleConid })
: connections.singleConnection;
let configurationError = null;
if (process.env.STORAGE_DATABASE && process.env.BASIC_AUTH) {
configurationError =
'Basic authentization is not allowed, when using storage. Cannot use both STORAGE_DATABASE and BASIC_AUTH';
}
const checkedLicense = await checkLicense();
const isLicenseValid = checkedLicense?.status == 'ok';
return {
runAsPortal: !!connections.portalConnections,
singleDatabase: connections.singleDatabase,
singleDbConnection: connections.singleDbConnection,
singleConnection: singleConnection,
isUserLoggedIn,
// hideAppEditor: !!process.env.HIDE_APP_EDITOR,
allowShellConnection: platformInfo.allowShellConnection,
allowShellScripting: platformInfo.allowShellScripting,
isDocker: platformInfo.isDocker,
isElectron: platformInfo.isElectron,
isLicenseValid,
checkedLicense,
configurationError,
logoutUrl: await authProvider.getLogoutUrl(),
permissions,
login,
// ...additionalConfigProps,
isBasicAuth: !!process.env.BASIC_AUTH,
isAdminLoginForm: !!(
process.env.STORAGE_DATABASE &&
process.env.ADMIN_PASSWORD &&
!process.env.BASIC_AUTH &&
checkedLicense?.type == 'premium'
),
storageDatabase: process.env.STORAGE_DATABASE,
logsFilePath: getLogsFilePath(),
connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
...currentVersion,
};
},
@@ -87,12 +124,39 @@ module.exports = {
async loadSettings() {
try {
const settingsText = await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' });
return this.fillMissingSettings(JSON.parse(settingsText));
return {
...this.fillMissingSettings(JSON.parse(settingsText)),
'other.licenseKey': platformInfo.isElectron ? await this.loadLicenseKey() : undefined,
};
} catch (err) {
return this.fillMissingSettings({});
}
},
async loadLicenseKey() {
try {
const licenseKey = await fs.readFile(path.join(datadir(), 'license.key'), { encoding: 'utf-8' });
return licenseKey;
} catch (err) {
return null;
}
},
saveLicenseKey_meta: true,
async saveLicenseKey({ licenseKey }) {
try {
if (process.env.STORAGE_DATABASE) {
await storage.writeConfig({ group: 'license', config: { licenseKey } });
// await storageWriteConfig('license', { licenseKey });
} else {
await fs.writeFile(path.join(datadir(), 'license.key'), licenseKey);
}
socket.emitChanged(`config-changed`);
} catch (err) {
return null;
}
},
updateSettings_meta: true,
async updateSettings(values, req) {
if (!hasPermission(`settings/change`, req)) return false;
@@ -102,10 +166,16 @@ module.exports = {
try {
const updated = {
...currentValue,
...values,
..._.omit(values, ['other.licenseKey']),
};
await fs.writeFile(path.join(datadir(), 'settings.json'), JSON.stringify(updated, undefined, 2));
// this.settingsValue = updated;
if (currentValue['other.licenseKey'] != values['other.licenseKey']) {
await this.saveLicenseKey({ licenseKey: values['other.licenseKey'] });
socket.emitChanged(`config-changed`);
}
socket.emitChanged(`settings-changed`);
return updated;
} catch (err) {
@@ -120,4 +190,10 @@ module.exports = {
const resp = await axios.default.get('https://raw.githubusercontent.com/dbgate/dbgate/master/CHANGELOG.md');
return resp.data;
},
checkLicense_meta: true,
async checkLicense({ licenseKey }) {
const resp = await checkLicenseKey(licenseKey);
return resp;
},
};

View File

@@ -2,6 +2,7 @@ const path = require('path');
const { fork } = require('child_process');
const _ = require('lodash');
const fs = require('fs-extra');
const crypto = require('crypto');
const { datadir, filesdir } = require('../utility/directories');
const socket = require('../utility/socket');
@@ -11,9 +12,17 @@ const { pickSafeConnectionInfo } = require('../utility/crypting');
const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
const processArgs = require('../utility/processArgs');
const { safeJsonParse } = require('dbgate-tools');
const { safeJsonParse, getLogger } = require('dbgate-tools');
const platformInfo = require('../utility/platformInfo');
const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission');
const pipeForkLogs = require('../utility/pipeForkLogs');
const requireEngineDriver = require('../utility/requireEngineDriver');
const { getAuthProviderById } = require('../auth/authProvider');
const { startTokenChecking } = require('../utility/authProxy');
const logger = getLogger('connections');
let volatileConnections = {};
function getNamedArgs() {
const res = {};
@@ -49,10 +58,14 @@ function getPortalCollections() {
server: process.env[`SERVER_${id}`],
user: process.env[`USER_${id}`],
password: process.env[`PASSWORD_${id}`],
passwordMode: process.env[`PASSWORD_MODE_${id}`],
port: process.env[`PORT_${id}`],
databaseUrl: process.env[`URL_${id}`],
useDatabaseUrl: !!process.env[`URL_${id}`],
databaseFile: process.env[`FILE_${id}`],
socketPath: process.env[`SOCKET_PATH_${id}`],
serviceName: process.env[`SERVICE_NAME_${id}`],
authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
defaultDatabase:
process.env[`DATABASE_${id}`] ||
(process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
@@ -60,6 +73,9 @@ function getPortalCollections() {
displayName: process.env[`LABEL_${id}`],
isReadOnly: process.env[`READONLY_${id}`],
databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
allowedDatabases: process.env[`ALLOWED_DATABASES_${id}`]?.replace(/\|/g, '\n'),
allowedDatabasesRegex: process.env[`ALLOWED_DATABASES_REGEX_${id}`],
parent: process.env[`PARENT_${id}`] || undefined,
// SSH tunnel
useSshTunnel: process.env[`USE_SSH_${id}`],
@@ -78,14 +94,15 @@ function getPortalCollections() {
sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
trustServerCertificate: process.env[`SSL_TRUST_CERTIFICATE_${id}`],
}));
console.log('Using connections from ENV variables:');
console.log(JSON.stringify(connections.map(pickSafeConnectionInfo), undefined, 2));
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'Using connections from ENV variables');
const noengine = connections.filter(x => !x.engine);
if (noengine.length > 0) {
console.log(
'Warning: Invalid CONNECTIONS configutation, missing ENGINE for connection ID:',
noengine.map(x => x._id)
logger.warn(
{ connections: noengine.map(x => x._id) },
'Invalid CONNECTIONS configutation, missing ENGINE for connection ID'
);
}
return connections;
@@ -123,9 +140,10 @@ function getPortalCollections() {
return null;
}
const portalConnections = getPortalCollections();
function getSingleDatabase() {
function getSingleDbConnection() {
if (process.env.SINGLE_CONNECTION && process.env.SINGLE_DATABASE) {
// @ts-ignore
const connection = portalConnections.find(x => x._id == process.env.SINGLE_CONNECTION);
@@ -149,12 +167,31 @@ function getSingleDatabase() {
return null;
}
const singleDatabase = getSingleDatabase();
function getSingleConnection() {
if (getSingleDbConnection()) return null;
if (process.env.SINGLE_CONNECTION) {
// @ts-ignore
const connection = portalConnections.find(x => x._id == process.env.SINGLE_CONNECTION);
if (connection) {
return connection;
}
}
// @ts-ignore
const arg0 = (portalConnections || []).find(x => x._id == 'argv');
if (arg0) {
return arg0;
}
return null;
}
const singleDbConnection = getSingleDbConnection();
const singleConnection = getSingleConnection();
module.exports = {
datastore: null,
opened: [],
singleDatabase,
singleDbConnection,
singleConnection,
portalConnections,
async _init() {
@@ -167,6 +204,12 @@ module.exports = {
list_meta: true,
async list(_params, req) {
const storage = require('./storage');
const storageConnections = await storage.connections(req);
if (storageConnections) {
return storageConnections;
}
if (portalConnections) {
if (platformInfo.allowShellConnection) return portalConnections;
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, req));
@@ -176,13 +219,20 @@ module.exports = {
test_meta: true,
test(connection) {
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'connectProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
]);
const subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
'--is-forked-api',
'--start-process',
'connectProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
],
{
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
pipeForkLogs(subprocess);
subprocess.send(connection);
return new Promise(resolve => {
subprocess.on('message', resp => {
@@ -196,6 +246,38 @@ module.exports = {
});
},
saveVolatile_meta: true,
async saveVolatile({ conid, user = undefined, password = undefined, accessToken = undefined, test = false }) {
const old = await this.getCore({ conid });
const res = {
...old,
_id: crypto.randomUUID(),
password,
accessToken,
passwordMode: undefined,
unsaved: true,
useRedirectDbLogin: false,
};
if (old.passwordMode == 'askUser') {
res.user = user;
}
if (test) {
const testRes = await this.test(res);
if (testRes.msgtype == 'connected') {
volatileConnections[res._id] = res;
return {
...res,
msgtype: 'connected',
};
}
return testRes;
} else {
volatileConnections[res._id] = res;
return res;
}
},
save_meta: true,
async save(connection) {
if (portalConnections) return;
@@ -226,6 +308,14 @@ module.exports = {
return res;
},
batchChangeFolder_meta: true,
async batchChangeFolder({ folder, newFolder }, req) {
// const updated = await this.datastore.find(x => x.parent == folder);
const res = await this.datastore.updateAll(x => (x.parent == folder ? { ...x, parent: newFolder } : x));
socket.emitChanged('connection-list-changed');
return res;
},
updateDatabase_meta: true,
async updateDatabase({ conid, database, values }, req) {
if (portalConnections) return;
@@ -255,6 +345,18 @@ module.exports = {
async getCore({ conid, mask = false }) {
if (!conid) return null;
const volatile = volatileConnections[conid];
if (volatile) {
return volatile;
}
const storage = require('./storage');
const storageConnection = await storage.getConnection({ conid });
if (storageConnection) {
return storageConnection;
}
if (portalConnections) {
const res = portalConnections.find(x => x._id == conid) || null;
return mask && !platformInfo.allowShellConnection ? maskConnection(res) : res;
@@ -284,4 +386,95 @@ module.exports = {
});
return res;
},
dbloginWeb_meta: {
raw: true,
method: 'get',
},
async dbloginWeb(req, res) {
const { conid, state, redirectUri } = req.query;
const connection = await this.getCore({ conid });
const driver = requireEngineDriver(connection);
const authResp = await driver.getRedirectAuthUrl(connection, {
redirectUri,
state,
client: 'web',
});
res.redirect(authResp.url);
},
dbloginApp_meta: true,
async dbloginApp({ conid, state }) {
const connection = await this.getCore({ conid });
const driver = requireEngineDriver(connection);
const resp = await driver.getRedirectAuthUrl(connection, {
state,
client: 'app',
});
startTokenChecking(resp.sid, async token => {
const volatile = await this.saveVolatile({ conid, accessToken: token });
socket.emit('got-volatile-token', { savedConId: conid, volatileConId: volatile._id });
});
return resp;
},
dbloginToken_meta: true,
async dbloginToken({ code, conid, strmid, redirectUri, sid }) {
try {
const connection = await this.getCore({ conid });
const driver = requireEngineDriver(connection);
const accessToken = await driver.getAuthTokenFromCode(connection, { sid, code, redirectUri });
const volatile = await this.saveVolatile({ conid, accessToken });
// console.log('******************************** WE HAVE ACCESS TOKEN', accessToken);
socket.emit('got-volatile-token', { strmid, savedConId: conid, volatileConId: volatile._id });
return { success: true };
} catch (err) {
logger.error({ err }, 'Error getting DB token');
return { error: err.message };
}
},
dbloginAuthToken_meta: true,
async dbloginAuthToken({ amoid, code, conid, redirectUri, sid }) {
try {
const connection = await this.getCore({ conid });
const driver = requireEngineDriver(connection);
const accessToken = await driver.getAuthTokenFromCode(connection, { code, redirectUri, sid });
const volatile = await this.saveVolatile({ conid, accessToken });
const authProvider = getAuthProviderById(amoid);
const resp = await authProvider.login(null, null, { conid: volatile._id });
return resp;
} catch (err) {
logger.error({ err }, 'Error getting DB token');
return { error: err.message };
}
},
dbloginAuth_meta: true,
async dbloginAuth({ amoid, conid, user, password }) {
if (user || password) {
const saveResp = await this.saveVolatile({ conid, user, password, test: true });
if (saveResp.msgtype == 'connected') {
const loginResp = await getAuthProviderById(amoid).login(user, password, { conid: saveResp._id });
return loginResp;
}
return saveResp;
}
// user and password is stored in connection, volatile connection is not needed
const loginResp = await getAuthProviderById(amoid).login(null, null, { conid });
return loginResp;
},
volatileDbloginFromAuth_meta: true,
async volatileDbloginFromAuth({ conid }, req) {
const connection = await this.getCore({ conid });
const driver = requireEngineDriver(connection);
const accessToken = await driver.getAccessTokenFromAuth(connection, req);
if (accessToken) {
const volatile = await this.saveVolatile({ conid, accessToken });
return volatile;
}
return null;
},
};

View File

@@ -1,4 +1,3 @@
const uuidv1 = require('uuid/v1');
const connections = require('./connections');
const archive = require('./archive');
const socket = require('../utility/socket');
@@ -12,6 +11,7 @@ const {
matchPairedObjects,
extendDatabaseInfo,
modelCompareDbDiffOptions,
getLogger,
} = require('dbgate-tools');
const { html, parse } = require('diff2html');
const { handleProcessCommunication } = require('../utility/processComm');
@@ -27,6 +27,11 @@ const { createTwoFilesPatch } = require('diff');
const diff2htmlPage = require('../utility/diff2htmlPage');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
const { MissingCredentialsError } = require('../utility/exceptions');
const pipeForkLogs = require('../utility/pipeForkLogs');
const crypto = require('crypto');
const logger = getLogger('databaseConnections');
module.exports = {
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
@@ -42,24 +47,24 @@ module.exports = {
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}`);
socket.emitChanged('database-structure-changed', { conid, database });
},
handle_structureTime(conid, database, { analysedTime }) {
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (!existing) return;
existing.analysedTime = analysedTime;
socket.emitChanged(`database-status-changed-${conid}-${database}`);
socket.emitChanged(`database-status-changed`, { conid, database });
},
handle_version(conid, database, { version }) {
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (!existing) return;
existing.serverVersion = version;
socket.emitChanged(`database-server-version-changed-${conid}-${database}`);
socket.emitChanged(`database-server-version-changed`, { conid, database });
},
handle_error(conid, database, props) {
const { error } = props;
console.log(`Error in database connection ${conid}, database ${database}: ${error}`);
logger.error(`Error in database connection ${conid}, database ${database}: ${error}`);
},
handle_response(conid, database, { msgid, ...response }) {
const [resolve, reject] = this.requests[msgid];
@@ -72,7 +77,7 @@ module.exports = {
if (!existing) return;
if (existing.status && status && existing.status.counter > status.counter) return;
existing.status = status;
socket.emitChanged(`database-status-changed-${conid}-${database}`);
socket.emitChanged(`database-status-changed`, { conid, database });
},
handle_ping() {},
@@ -81,13 +86,26 @@ module.exports = {
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) return existing;
const connection = await connections.getCore({ conid });
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'databaseConnectionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
]);
if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
}
if (connection.useRedirectDbLogin) {
throw new MissingCredentialsError({ conid, redirectToDbLogin: true });
}
const subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
'--is-forked-api',
'--start-process',
'databaseConnectionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
],
{
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
pipeForkLogs(subprocess);
const lastClosed = this.closed[`${conid}/${database}`];
const newOpened = {
conid,
@@ -122,10 +140,15 @@ module.exports = {
/** @param {import('dbgate-types').OpenedDatabaseConnection} conn */
sendRequest(conn, message) {
const msgid = uuidv1();
const msgid = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
this.requests[msgid] = [resolve, reject];
conn.subprocess.send({ msgid, ...message });
try {
conn.subprocess.send({ msgid, ...message });
} catch (err) {
logger.error({ err }, 'Error sending request do process');
this.close(conn.conid, conn.database);
}
});
return promise;
},
@@ -133,7 +156,7 @@ module.exports = {
queryData_meta: true,
async queryData({ conid, database, sql }, req) {
testConnectionPermission(conid, req);
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
logger.info({ conid, database, sql }, 'Processing query');
const opened = await this.ensureOpened(conid, database);
// if (opened && opened.status && opened.status.name == 'error') {
// return opened.status;
@@ -151,11 +174,20 @@ module.exports = {
},
runScript_meta: true,
async runScript({ conid, database, sql }, req) {
async runScript({ conid, database, sql, useTransaction }, req) {
testConnectionPermission(conid, req);
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
logger.info({ conid, database, sql }, 'Processing script');
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql });
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql, useTransaction });
return res;
},
runOperation_meta: true,
async runOperation({ conid, database, operation, useTransaction }, req) {
testConnectionPermission(conid, req);
logger.info({ conid, database, operation }, 'Processing operation');
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'runOperation', operation, useTransaction });
return res;
},
@@ -272,8 +304,19 @@ module.exports = {
let existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
existing.subprocess.send({ msgtype: 'ping' });
try {
existing.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error({ err }, 'Error pinging DB connection');
this.close(conid, database);
return {
status: 'error',
message: 'Ping failed',
};
}
} else {
// @ts-ignore
existing = await this.ensureOpened(conid, database);
}
@@ -304,7 +347,13 @@ module.exports = {
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
existing.disconnected = true;
if (kill) existing.subprocess.kill();
if (kill) {
try {
existing.subprocess.kill();
} catch (err) {
logger.error({ err }, 'Error killing subprocess');
}
}
this.opened = this.opened.filter(x => x.conid != conid || x.database != database);
this.closed[`${conid}/${database}`] = {
status: {
@@ -313,7 +362,7 @@ module.exports = {
},
structure: existing.structure,
};
socket.emitChanged(`database-status-changed-${conid}-${database}`);
socket.emitChanged(`database-status-changed`, { conid, database });
}
},

View File

@@ -1,6 +1,6 @@
const uuidv1 = require('uuid/v1');
const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
const getChartExport = require('../utility/getChartExport');
const { hasPermission } = require('../utility/hasPermission');
@@ -49,7 +49,7 @@ module.exports = {
async delete({ folder, file }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
await fs.unlink(path.join(filesdir(), folder, file));
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
return true;
},
@@ -58,7 +58,7 @@ module.exports = {
async rename({ folder, file, newFile }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
await fs.rename(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
return true;
},
@@ -66,7 +66,7 @@ module.exports = {
refresh_meta: true,
async refresh({ folders }, req) {
for (const folder of folders) {
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
}
return true;
@@ -76,7 +76,7 @@ module.exports = {
async copy({ folder, file, newFile }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
return true;
},
@@ -112,13 +112,13 @@ module.exports = {
if (!hasPermission(`archive/write`, req)) return false;
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
await fs.writeFile(path.join(dir, file), serialize(format, data));
socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`);
socket.emitChanged(`archive-files-changed`, { folder: folder.substring('archive:'.length) });
return true;
} else if (folder.startsWith('app:')) {
if (!hasPermission(`apps/write`, req)) return false;
const app = folder.substring('app:'.length);
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
socket.emitChanged(`app-files-changed-${app}`);
socket.emitChanged(`app-files-changed`, { app });
socket.emitChanged('used-apps-changed');
apps.emitChangedDbApp(folder);
return true;
@@ -129,7 +129,7 @@ module.exports = {
await fs.mkdir(dir);
}
await fs.writeFile(path.join(dir, file), serialize(format, data));
socket.emitChanged(`files-changed-${folder}`);
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
if (folder == 'shell') {
scheduler.reload();
@@ -164,7 +164,7 @@ module.exports = {
generateUploadsFile_meta: true,
async generateUploadsFile({ extension }) {
const fileName = `${uuidv1()}.${extension || 'html'}`;
const fileName = `${crypto.randomUUID()}.${extension || 'html'}`;
return {
fileName,
filePath: path.join(uploadsdir(), fileName),

View File

@@ -4,9 +4,9 @@ const lineReader = require('line-reader');
const _ = require('lodash');
const { __ } = require('lodash/fp');
const DatastoreProxy = require('../utility/DatastoreProxy');
const { saveFreeTableData } = require('../utility/freeTableStorage');
const getJslFileName = require('../utility/getJslFileName');
const JsonLinesDatastore = require('../utility/JsonLinesDatastore');
const requirePluginFunction = require('../utility/requirePluginFunction');
const socket = require('../utility/socket');
function readFirstLine(file) {
@@ -99,16 +99,27 @@ module.exports = {
// return readerInfo;
// },
async ensureDatastore(jslid) {
async ensureDatastore(jslid, formatterFunction) {
let datastore = this.datastores[jslid];
if (!datastore) {
datastore = new JsonLinesDatastore(getJslFileName(jslid));
if (!datastore || datastore.formatterFunction != formatterFunction) {
if (datastore) {
datastore._closeReader();
}
datastore = new JsonLinesDatastore(getJslFileName(jslid), formatterFunction);
// datastore = new DatastoreProxy(getJslFileName(jslid));
this.datastores[jslid] = datastore;
}
return datastore;
},
async closeDataStore(jslid) {
const datastore = this.datastores[jslid];
if (datastore) {
await datastore._closeReader();
delete this.datastores[jslid];
}
},
getInfo_meta: true,
async getInfo({ jslid }) {
const file = getJslFileName(jslid);
@@ -131,9 +142,15 @@ module.exports = {
},
getRows_meta: true,
async getRows({ jslid, offset, limit, filters }) {
const datastore = await this.ensureDatastore(jslid);
return datastore.getRows(offset, limit, _.isEmpty(filters) ? null : filters);
async getRows({ jslid, offset, limit, filters, sort, formatterFunction }) {
const datastore = await this.ensureDatastore(jslid, formatterFunction);
return datastore.getRows(offset, limit, _.isEmpty(filters) ? null : filters, _.isEmpty(sort) ? null : sort);
},
exists_meta: true,
async exists({ jslid }) {
const fileName = getJslFileName(jslid);
return fs.existsSync(fileName);
},
getStats_meta: true,
@@ -150,8 +167,8 @@ module.exports = {
},
loadFieldValues_meta: true,
async loadFieldValues({ jslid, field, search }) {
const datastore = await this.ensureDatastore(jslid);
async loadFieldValues({ jslid, field, search, formatterFunction }) {
const datastore = await this.ensureDatastore(jslid, formatterFunction);
const res = new Set();
await datastore.enumRows(row => {
if (!filterName(search, row[field])) return true;
@@ -177,15 +194,100 @@ module.exports = {
// }
},
saveFreeTable_meta: true,
async saveFreeTable({ jslid, data }) {
saveFreeTableData(getJslFileName(jslid), data);
return true;
},
saveText_meta: true,
async saveText({ jslid, text }) {
await fs.promises.writeFile(getJslFileName(jslid), text);
return true;
},
saveRows_meta: true,
async saveRows({ jslid, rows }) {
const fileStream = fs.createWriteStream(getJslFileName(jslid));
for (const row of rows) {
await fileStream.write(JSON.stringify(row) + '\n');
}
await fileStream.close();
return true;
},
extractTimelineChart_meta: true,
async extractTimelineChart({ jslid, timestampFunction, aggregateFunction, measures }) {
const timestamp = requirePluginFunction(timestampFunction);
const aggregate = requirePluginFunction(aggregateFunction);
const datastore = new JsonLinesDatastore(getJslFileName(jslid));
let mints = null;
let maxts = null;
// pass 1 - counts stats, time range
await datastore.enumRows(row => {
const ts = timestamp(row);
if (!mints || ts < mints) mints = ts;
if (!maxts || ts > maxts) maxts = ts;
return true;
});
const minTime = new Date(mints).getTime();
const maxTime = new Date(maxts).getTime();
const duration = maxTime - minTime;
const STEPS = 100;
let stepCount = duration > 100 * 1000 ? STEPS : Math.round((maxTime - minTime) / 1000);
if (stepCount < 2) {
stepCount = 2;
}
const stepDuration = duration / stepCount;
const labels = _.range(stepCount).map(i => new Date(minTime + stepDuration / 2 + stepDuration * i));
// const datasets = measures.map(m => ({
// label: m.label,
// data: Array(stepCount).fill(0),
// }));
const mproc = measures.map(m => ({
...m,
}));
const data = Array(stepCount)
.fill(0)
.map(() => ({}));
// pass 2 - count measures
await datastore.enumRows(row => {
const ts = timestamp(row);
let part = Math.round((new Date(ts).getTime() - minTime) / stepDuration);
if (part < 0) part = 0;
if (part >= stepCount) part - stepCount - 1;
if (data[part]) {
data[part] = aggregate(data[part], row, stepDuration);
}
return true;
});
datastore._closeReader();
// const measureByField = _.fromPairs(measures.map((m, i) => [m.field, i]));
// for (let mindex = 0; mindex < measures.length; mindex++) {
// for (let stepIndex = 0; stepIndex < stepCount; stepIndex++) {
// const measure = measures[mindex];
// if (measure.perSecond) {
// datasets[mindex].data[stepIndex] /= stepDuration / 1000;
// }
// if (measure.perField) {
// datasets[mindex].data[stepIndex] /= datasets[measureByField[measure.perField]].data[stepIndex];
// }
// }
// }
// for (let i = 0; i < measures.length; i++) {
// if (measures[i].hidden) {
// datasets[i] = null;
// }
// }
return {
labels,
datasets: mproc.map(m => ({
label: m.label,
data: data.map(d => d[m.field] || 0),
})),
};
},
};

View File

@@ -1,15 +1,22 @@
const crypto = require('crypto');
const _ = require('lodash');
const path = require('path');
const fs = require('fs-extra');
const uuidv1 = require('uuid/v1');
const byline = require('byline');
const socket = require('../utility/socket');
const { fork } = require('child_process');
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
const { extractShellApiPlugins, extractShellApiFunctionName, jsonScriptToJavascript } = require('dbgate-tools');
const {
extractShellApiPlugins,
extractShellApiFunctionName,
jsonScriptToJavascript,
getLogger,
safeJsonParse,
} = require('dbgate-tools');
const { handleProcessCommunication } = require('../utility/processComm');
const processArgs = require('../utility/processArgs');
const platformInfo = require('../utility/platformInfo');
const logger = getLogger('runners');
function extractPlugins(script) {
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
@@ -29,13 +36,14 @@ const requirePluginsTemplate = (plugins, isExport) =>
const scriptTemplate = (script, isExport) => `
const dbgateApi = require(${isExport ? `'dbgate-api'` : 'process.env.DBGATE_API'});
const logger = dbgateApi.getLogger('script');
dbgateApi.initializeApiEnvironment();
${requirePluginsTemplate(extractPlugins(script), isExport)}
require=null;
async function run() {
${script}
await dbgateApi.finalizer.run();
console.log('Finished job script');
logger.info('Finished job script');
}
dbgateApi.runScript(run);
`;
@@ -59,20 +67,23 @@ module.exports = {
requests: {},
dispatchMessage(runid, message) {
if (message) console.log('...', message.message);
if (_.isString(message)) {
socket.emit(`runner-info-${runid}`, {
message,
if (message) {
const json = safeJsonParse(message.message);
if (json) logger.log(json);
else logger.info(message.message);
const toEmit = {
time: new Date(),
severity: 'info',
});
}
if (_.isPlainObject(message)) {
socket.emit(`runner-info-${runid}`, {
time: new Date(),
severity: 'info',
...message,
});
message: json ? json.msg : message.message,
};
if (json && json.level >= 50) {
toEmit.severity = 'error';
}
socket.emit(`runner-info-${runid}`, toEmit);
}
},
@@ -98,13 +109,15 @@ module.exports = {
fs.writeFileSync(`${scriptFile}`, scriptText);
fs.mkdirSync(directory);
const pluginNames = _.union(fs.readdirSync(pluginsdir()), packagedPluginList);
console.log(`RUNNING SCRIPT ${scriptFile}`);
logger.info({ scriptFile }, 'Running script');
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
const subprocess = fork(
scriptFile,
[
'--checkParent', // ...process.argv.slice(3)
'--is-forked-api',
'--process-display-name',
'script',
...processArgs.getPassArgs(),
],
{
@@ -117,14 +130,15 @@ module.exports = {
},
}
);
const pipeDispatcher = severity => data =>
this.dispatchMessage(runid, { severity, message: data.toString().trim() });
const pipeDispatcher = severity => data => {
return 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 => {
this.rejectRequest(runid, { message: 'No data retured, maybe input data source is too big' });
console.log('... EXIT process', code);
logger.info({ code, pid: subprocess.pid }, 'Exited process');
socket.emit(`runner-done-${runid}`, code);
});
subprocess.on('error', error => {
@@ -151,7 +165,7 @@ module.exports = {
start_meta: true,
async start({ script }) {
const runid = uuidv1();
const runid = crypto.randomUUID()
if (script.type == 'json') {
const js = jsonScriptToJavascript(script);
@@ -199,7 +213,7 @@ module.exports = {
loadReader_meta: true,
async loadReader({ functionName, props }) {
const promise = new Promise((resolve, reject) => {
const runid = uuidv1();
const runid = crypto.randomUUID();
this.requests[runid] = [resolve, reject];
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
});

View File

@@ -4,6 +4,9 @@ const path = require('path');
const cron = require('node-cron');
const runners = require('./runners');
const { hasPermission } = require('../utility/hasPermission');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('scheduler');
const scheduleRegex = /\s*\/\/\s*@schedule\s+([^\n]+)\n/;
@@ -21,7 +24,7 @@ module.exports = {
if (!match) return;
const pattern = match[1];
if (!cron.validate(pattern)) return;
console.log(`Schedule script ${file} with pattern ${pattern}`);
logger.info(`Schedule script ${file} with pattern ${pattern}`);
const task = cron.schedule(pattern, () => runners.start({ script: text }));
this.tasks.push(task);
},

View File

@@ -8,23 +8,29 @@ const lock = new AsyncLock();
const config = require('./config');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
const { MissingCredentialsError } = require('../utility/exceptions');
const pipeForkLogs = require('../utility/pipeForkLogs');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('serverConnection');
module.exports = {
opened: [],
closed: {},
lastPinged: {},
requests: {},
handle_databases(conid, { databases }) {
const existing = this.opened.find(x => x.conid == conid);
if (!existing) return;
existing.databases = databases;
socket.emitChanged(`database-list-changed-${conid}`);
socket.emitChanged(`database-list-changed`, { conid });
},
handle_version(conid, { version }) {
const existing = this.opened.find(x => x.conid == conid);
if (!existing) return;
existing.version = version;
socket.emitChanged(`server-version-changed-${conid}`);
socket.emitChanged(`server-version-changed`, { conid });
},
handle_status(conid, { status }) {
const existing = this.opened.find(x => x.conid == conid);
@@ -33,19 +39,40 @@ module.exports = {
socket.emitChanged(`server-status-changed`);
},
handle_ping() {},
handle_response(conid, { msgid, ...response }) {
const [resolve, reject] = this.requests[msgid];
resolve(response);
delete this.requests[msgid];
},
async ensureOpened(conid) {
const res = await lock.acquire(conid, async () => {
const existing = this.opened.find(x => x.conid == conid);
if (existing) return existing;
const connection = await connections.getCore({ conid });
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'serverConnectionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
]);
if (!connection) {
throw new Error(`Connection with conid="${conid}" not fund`);
}
if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
}
if (connection.useRedirectDbLogin) {
throw new MissingCredentialsError({ conid, redirectToDbLogin: true });
}
const subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
'--is-forked-api',
'--start-process',
'serverConnectionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
],
{
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
pipeForkLogs(subprocess);
const newOpened = {
conid,
subprocess,
@@ -80,7 +107,13 @@ module.exports = {
const existing = this.opened.find(x => x.conid == conid);
if (existing) {
existing.disconnected = true;
if (kill) existing.subprocess.kill();
if (kill) {
try {
existing.subprocess.kill();
} catch (err) {
logger.error({ err }, 'Error killing subprocess');
}
}
this.opened = this.opened.filter(x => x.conid != conid);
this.closed[conid] = {
...existing.status,
@@ -99,6 +132,7 @@ module.exports = {
listDatabases_meta: true,
async listDatabases({ conid }, req) {
if (!conid) return [];
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
return opened.databases;
@@ -120,18 +154,24 @@ module.exports = {
},
ping_meta: true,
async ping({ connections }) {
async ping({ conidArray, strmid }) {
await Promise.all(
_.uniq(connections).map(async conid => {
_.uniq(conidArray).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' });
try {
opened.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error({ err }, 'Error pinging server connection');
this.close(conid);
}
})
);
socket.setStreamIdFilter(strmid, { conid: conidArray });
return { status: 'ok' };
},
@@ -152,4 +192,71 @@ module.exports = {
opened.subprocess.send({ msgtype: 'createDatabase', name });
return { status: 'ok' };
},
dropDatabase_meta: true,
async dropDatabase({ conid, name }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (opened.connection.isReadOnly) return false;
opened.subprocess.send({ msgtype: 'dropDatabase', name });
return { status: 'ok' };
},
sendRequest(conn, message) {
const msgid = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
this.requests[msgid] = [resolve, reject];
try {
conn.subprocess.send({ msgid, ...message });
} catch (err) {
logger.error({ err }, 'Error sending request');
this.close(conn.conid);
}
});
return promise;
},
async loadDataCore(msgtype, { conid, ...args }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
const res = await this.sendRequest(opened, { msgtype, ...args });
if (res.errorMessage) {
console.error(res.errorMessage);
return {
errorMessage: res.errorMessage,
};
}
return res.result || null;
},
serverSummary_meta: true,
async serverSummary({ conid }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('serverSummary', { conid });
},
summaryCommand_meta: true,
async summaryCommand({ conid, command, row }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (opened.connection.isReadOnly) return false;
return this.loadDataCore('summaryCommand', { conid, command, row });
},
getOpenedConnectionReport() {
return this.opened.map(con => ({
status: con.status,
versionText: con.version?.versionText,
databaseCount: con.databases.length,
connection: _.pick(con.connection, [
'engine',
'useSshTunnel',
'authType',
'trustServerCertificate',
'useSsl',
'sshMode',
]),
}));
},
};

View File

@@ -1,5 +1,5 @@
const crypto = require('crypto');
const _ = require('lodash');
const uuidv1 = require('uuid/v1');
const connections = require('./connections');
const socket = require('../utility/socket');
const { fork } = require('child_process');
@@ -8,6 +8,11 @@ const path = require('path');
const { handleProcessCommunication } = require('../utility/processComm');
const processArgs = require('../utility/processArgs');
const { appdir } = require('../utility/directories');
const { getLogger } = require('dbgate-tools');
const pipeForkLogs = require('../utility/pipeForkLogs');
const config = require('./config');
const logger = getLogger('sessions');
module.exports = {
/** @type {import('dbgate-types').OpenedSession[]} */
@@ -80,15 +85,22 @@ module.exports = {
create_meta: true,
async create({ conid, database }) {
const sesid = uuidv1();
const sesid = crypto.randomUUID();
const connection = await connections.getCore({ conid });
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'sessionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
]);
const subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
'--is-forked-api',
'--start-process',
'sessionProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
],
{
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
pipeForkLogs(subprocess);
const newOpened = {
conid,
database,
@@ -103,7 +115,18 @@ module.exports = {
if (handleProcessCommunication(message, subprocess)) return;
this[`handle_${msgtype}`](sesid, message);
});
subprocess.send({ msgtype: 'connect', ...connection, database });
subprocess.on('exit', () => {
this.opened = this.opened.filter(x => x.sesid != sesid);
this.dispatchMessage(sesid, 'Query session closed');
socket.emit(`session-closed-${sesid}`);
});
subprocess.send({
msgtype: 'connect',
...connection,
database,
globalSettings: await config.getSettings(),
});
return _.pick(newOpened, ['conid', 'database', 'sesid']);
},
@@ -114,7 +137,7 @@ module.exports = {
throw new Error('Invalid session');
}
console.log(`Processing query, sesid=${sesid}, sql=${sql}`);
logger.info({ sesid, sql }, 'Processing query');
this.dispatchMessage(sesid, 'Query execution started');
session.subprocess.send({ msgtype: 'executeQuery', sql });
@@ -126,7 +149,7 @@ module.exports = {
const { sesid } = await this.create({ conid, database });
const session = this.opened.find(x => x.sesid == sesid);
session.killOnDone = true;
const jslid = uuidv1();
const jslid = crypto.randomUUID();
session.loadingReader_jslid = jslid;
const fileName = queryName && appFolder ? path.join(appdir(), appFolder, `${queryName}.query.sql`) : null;
@@ -144,6 +167,31 @@ module.exports = {
return true;
},
startProfiler_meta: true,
async startProfiler({ sesid }) {
const jslid = crypto.randomUUID();
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
logger.info({ sesid }, 'Starting profiler');
session.loadingReader_jslid = jslid;
session.subprocess.send({ msgtype: 'startProfiler', jslid });
return { state: 'ok', jslid };
},
stopProfiler_meta: true,
async stopProfiler({ sesid }) {
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
session.subprocess.send({ msgtype: 'stopProfiler' });
return { state: 'ok' };
},
// cancel_meta: true,
// async cancel({ sesid }) {
// const session = this.opened.find((x) => x.sesid == sesid);
@@ -165,6 +213,26 @@ module.exports = {
return { state: 'ok' };
},
ping_meta: true,
async ping({ sesid }) {
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
try {
session.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error({ err }, 'Error pinging session');
return {
status: 'error',
message: 'Ping failed',
};
}
return { state: 'ok' };
},
// runCommand_meta: true,
// async runCommand({ conid, database, sql }) {
// console.log(`Running SQL command , conid=${conid}, database=${database}, sql=${sql}`);

View File

@@ -0,0 +1,20 @@
module.exports = {
connections_meta: true,
async connections(req) {
return null;
},
getConnection_meta: true,
async getConnection({ conid }) {
return null;
},
async loadSuperadminPermissions() {
return [];
},
getConnectionsForLoginPage_meta: true,
async getConnectionsForLoginPage() {
return null;
},
};

View File

@@ -1,6 +1,18 @@
const crypto = require('crypto');
const path = require('path');
const { uploadsdir } = require('../utility/directories');
const uuidv1 = require('uuid/v1');
const { uploadsdir, getLogsFilePath } = require('../utility/directories');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('uploads');
const axios = require('axios');
const os = require('os');
const fs = require('fs/promises');
const { read } = require('./queryHistory');
const platformInfo = require('../utility/platformInfo');
const _ = require('lodash');
const serverConnections = require('./serverConnections');
const config = require('./config');
const gistSecret = require('../gistSecret');
const currentVersion = require('../currentVersion');
module.exports = {
upload_meta: {
@@ -13,9 +25,9 @@ module.exports = {
res.json(null);
return;
}
const uploadName = uuidv1();
const uploadName = crypto.randomUUID();
const filePath = path.join(uploadsdir(), uploadName);
console.log(`Uploading file ${data.name}, size=${data.size}`);
logger.info(`Uploading file ${data.name}, size=${data.size}`);
data.mv(filePath, () => {
res.json({
@@ -33,4 +45,89 @@ module.exports = {
get(req, res) {
res.sendFile(path.join(uploadsdir(), req.query.file));
},
async getGistToken() {
const settings = await config.getSettings();
return settings['other.gistCreateToken'] || gistSecret;
},
uploadErrorToGist_meta: true,
async uploadErrorToGist() {
const logs = await fs.readFile(getLogsFilePath(), { encoding: 'utf-8' });
const connections = await serverConnections.getOpenedConnectionReport();
try {
const response = await axios.default.post(
'https://api.github.com/gists',
{
description: `DbGate ${currentVersion.version} error report`,
public: false,
files: {
'logs.jsonl': {
content: logs,
},
'os.json': {
content: JSON.stringify(
{
release: os.release(),
arch: os.arch(),
machine: os.machine(),
platform: os.platform(),
type: os.type(),
},
null,
2
),
},
'platform.json': {
content: JSON.stringify(
_.omit(
{
...platformInfo,
},
['defaultKeyfile', 'sshAuthSock']
),
null,
2
),
},
'connections.json': {
content: JSON.stringify(connections, null, 2),
},
'version.json': {
content: JSON.stringify(currentVersion, null, 2),
},
},
},
{
headers: {
Authorization: `token ${await this.getGistToken()}`,
'Content-Type': 'application/json',
Accept: 'application/vnd.github.v3+json',
},
}
);
return response.data;
} catch (err) {
logger.error({ err }, 'Error uploading gist');
return {
apiErrorMessage: err.message,
};
// console.error('Error creating gist:', error.response ? error.response.data : error.message);
}
},
deleteGist_meta: true,
async deleteGist({ url }) {
const response = await axios.default.delete(url, {
headers: {
Authorization: `token ${await this.getGistToken()}`,
'Content-Type': 'application/json',
Accept: 'application/vnd.github.v3+json',
},
});
return true;
},
};

View File

@@ -0,0 +1 @@
module.exports = process.env.GIST_UPLOAD_SECRET;

View File

@@ -1,14 +1,114 @@
const shell = require('./shell');
const { setLogConfig, getLogger, setLoggerName } = require('dbgate-tools');
const processArgs = require('./utility/processArgs');
const dbgateTools = require('dbgate-tools');
const fs = require('fs');
const moment = require('moment');
const path = require('path');
const { logsdir, setLogsFilePath, getLogsFilePath } = require('./utility/directories');
const { createLogger } = require('pinomin');
global['DBGATE_TOOLS'] = dbgateTools;
if (processArgs.startProcess) {
setLoggerName(processArgs.startProcess.replace(/Process$/, ''));
}
if (processArgs.processDisplayName) {
setLoggerName(processArgs.processDisplayName);
}
// function loadLogsContent(maxLines) {
// const text = fs.readFileSync(getLogsFilePath(), { encoding: 'utf8' });
// if (maxLines) {
// const lines = text
// .split('\n')
// .map(x => x.trim())
// .filter(x => x);
// return lines.slice(-maxLines).join('\n');
// }
// return text;
// }
function configureLogger() {
const logsFilePath = path.join(logsdir(), `${moment().format('YYYY-MM-DD-HH-mm')}-${process.pid}.ndjson`);
setLogsFilePath(logsFilePath);
setLoggerName('main');
const consoleLogLevel = process.env.CONSOLE_LOG_LEVEL || process.env.LOG_LEVEL || 'info';
const fileLogLevel = process.env.FILE_LOG_LEVEL || process.env.LOG_LEVEL || 'debug';
const logConfig = {
base: { pid: process.pid },
targets: [
{
type: 'console',
// @ts-ignore
level: consoleLogLevel,
},
{
type: 'stream',
// @ts-ignore
level: fileLogLevel,
stream: fs.createWriteStream(logsFilePath, { flags: 'a' }),
},
],
};
// logger.info(`Initialized logging, console log level: ${consoleLogLevel}, file log level: ${fileLogLevel}`);
// const streams = [];
// if (!platformInfo.isElectron) {
// streams.push({
// stream: process.stdout,
// level: process.env.CONSOLE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
// });
// }
// streams.push({
// stream: fs.createWriteStream(logsFilePath),
// level: process.env.FILE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
// });
// let logger = pinoms({
// redact: { paths: ['hostname'], remove: true },
// streams,
// });
// // @ts-ignore
// let logger = pino({
// redact: { paths: ['hostname'], remove: true },
// transport: {
// targets: [
// {
// level: process.env.CONSOLE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
// target: 'pino/file',
// },
// {
// level: process.env.FILE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
// target: 'pino/file',
// options: { destination: logsFilePath },
// },
// ],
// },
// });
// @ts-ignore
setLogConfig(logConfig);
}
if (processArgs.listenApi) {
configureLogger();
}
const shell = require('./shell/index');
const currentVersion = require('./currentVersion');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
};
if (processArgs.startProcess) {
const proc = require('./proc');
const module = proc[processArgs.startProcess];
module.start();
}
}
if (processArgs.listenApi) {
const main = require('./main');
@@ -17,5 +117,9 @@ if (processArgs.listenApi) {
module.exports = {
...shell,
getLogger,
configureLogger,
currentVersion,
// loadLogsContent,
getMainModule: () => require('./main'),
};

View File

@@ -18,19 +18,25 @@ const sessions = require('./controllers/sessions');
const runners = require('./controllers/runners');
const jsldata = require('./controllers/jsldata');
const config = require('./controllers/config');
const storage = require('./controllers/storage');
const archive = require('./controllers/archive');
const apps = require('./controllers/apps');
const auth = require('./controllers/auth');
const uploads = require('./controllers/uploads');
const plugins = require('./controllers/plugins');
const files = require('./controllers/files');
const scheduler = require('./controllers/scheduler');
const queryHistory = require('./controllers/queryHistory');
const onFinished = require('on-finished');
const { rundir } = require('./utility/directories');
const platformInfo = require('./utility/platformInfo');
const getExpressPath = require('./utility/getExpressPath');
const { getLogins } = require('./utility/hasPermission');
const _ = require('lodash');
const { getLogger } = require('dbgate-tools');
const { getDefaultAuthProvider } = require('./auth/authProvider');
const logger = getLogger('main');
function start() {
// console.log('process.argv', process.argv);
@@ -39,11 +45,23 @@ function start() {
const server = http.createServer(app);
const logins = getLogins();
if (logins) {
if (process.env.BASIC_AUTH && !process.env.STORAGE_DATABASE) {
async function authorizer(username, password, cb) {
try {
const resp = await getDefaultAuthProvider().login(username, password);
if (resp.accessToken) {
cb(null, true);
} else {
cb(null, false);
}
} catch (err) {
cb(err, false);
}
}
app.use(
basicAuth({
users: _.fromPairs(logins.map(x => [x.login, x.password])),
authorizer,
authorizeAsync: true,
challenge: true,
realm: 'DbGate Web App',
})
@@ -52,7 +70,25 @@ function start() {
app.use(cors());
if (platformInfo.isDocker) {
// server static files inside docker container
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
} else if (platformInfo.isNpmDist) {
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
} else if (process.env.DEVWEB) {
// console.log('__dirname', __dirname);
// console.log(path.join(__dirname, '../../web/public/build'));
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../web/public')));
} else {
app.get(getExpressPath('/'), (req, res) => {
res.send('DbGate API');
});
}
app.use(auth.authMiddleware);
app.get(getExpressPath('/stream'), async function (req, res) {
const strmid = req.query.strmid;
res.set({
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
@@ -63,7 +99,10 @@ function start() {
// Tell the client to retry every 10 seconds if connectivity is lost
res.write('retry: 10000\n\n');
socket.setSseResponse(res);
socket.addSseResponse(res, strmid);
onFinished(req, () => {
socket.removeSseResponse(strmid);
});
});
app.use(bodyParser.json({ limit: '50mb' }));
@@ -84,14 +123,10 @@ function start() {
app.use(getExpressPath('/runners/data'), express.static(rundir()));
if (platformInfo.isDocker) {
// server static files inside docker container
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
const port = process.env.PORT || 3000;
console.log('DbGate API listening on port (docker build)', port);
logger.info(`DbGate API listening on port ${port} (docker build)`);
server.listen(port);
} else if (platformInfo.isNpmDist) {
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
getPort({
port: parseInt(
// @ts-ignore
@@ -99,35 +134,27 @@ function start() {
),
}).then(port => {
server.listen(port, () => {
console.log(`DbGate API listening on port ${port} (NPM build)`);
logger.info(`DbGate API listening on port ${port} (NPM build)`);
});
});
} else if (process.env.DEVWEB) {
console.log('__dirname', __dirname);
console.log(path.join(__dirname, '../../web/public/build'));
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../web/public')));
const port = process.env.PORT || 3000;
console.log('DbGate API & web listening on port (dev web build)', port);
logger.info(`DbGate API & web listening on port ${port} (dev web build)`);
server.listen(port);
} else {
app.get(getExpressPath('/'), (req, res) => {
res.send('DbGate API');
});
const port = process.env.PORT || 3000;
console.log('DbGate API listening on port (dev API build)', port);
logger.info(`DbGate API listening on port ${port} (dev API build)`);
server.listen(port);
}
function shutdown() {
console.log('\nShutting down DbGate API server');
logger.info('\nShutting down DbGate API server');
server.close(() => {
console.log('Server shut down, terminating');
logger.info('Server shut down, terminating');
process.exit(0);
});
setTimeout(() => {
console.log('Server close timeout, terminating');
logger.info('Server close timeout, terminating');
process.exit(0);
}, 1000);
}
@@ -146,6 +173,7 @@ function useAllControllers(app, electron) {
useController(app, electron, '/runners', runners);
useController(app, electron, '/jsldata', jsldata);
useController(app, electron, '/config', config);
useController(app, electron, '/storage', storage);
useController(app, electron, '/archive', archive);
useController(app, electron, '/uploads', uploads);
useController(app, electron, '/plugins', plugins);
@@ -153,6 +181,7 @@ function useAllControllers(app, electron) {
useController(app, electron, '/scheduler', scheduler);
useController(app, electron, '/query-history', queryHistory);
useController(app, electron, '/apps', apps);
useController(app, electron, '/auth', auth);
}
function setElectronSender(electronSender) {

View File

@@ -1,7 +1,7 @@
const stableStringify = require('json-stable-stringify');
const { splitQuery } = require('dbgate-query-splitter');
const childProcessChecker = require('../utility/childProcessChecker');
const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools');
const { extractBoolSettingsValue, extractIntSettingsValue, getLogger } = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { handleProcessCommunication } = require('../utility/processComm');
@@ -9,6 +9,8 @@ const { SqlGenerator } = require('dbgate-tools');
const generateDeploySql = require('../shell/generateDeploySql');
const { dumpSqlSelect } = require('dbgate-sqltree');
const logger = getLogger('dbconnProcess');
let systemConnection;
let storedConnection;
let afterConnectCallbacks = [];
@@ -156,12 +158,24 @@ function resolveAnalysedPromises() {
afterAnalyseCallbacks = [];
}
async function handleRunScript({ msgid, sql }, skipReadonlyCheck = false) {
async function handleRunScript({ msgid, sql, useTransaction }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
await driver.script(systemConnection, sql);
await driver.script(systemConnection, sql, { useTransaction });
process.send({ msgtype: 'response', msgid });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleRunOperation({ msgid, operation, useTransaction }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
await driver.operation(systemConnection, operation, { useTransaction });
process.send({ msgtype: 'response', msgid });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
@@ -173,10 +187,11 @@ async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
const driver = requireEngineDriver(storedConnection);
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
// console.log(sql);
const res = await driver.query(systemConnection, sql);
process.send({ msgtype: 'response', msgid, ...res });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
process.send({ msgtype: 'response', msgid, errorMessage: err.message || 'Error executing SQL script' });
}
}
@@ -268,7 +283,7 @@ async function handleSqlPreview({ msgid, objects, options }) {
process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
if (generator.isUnhandledException) {
setTimeout(() => {
console.log('Exiting because of unhandled exception');
logger.error('Exiting because of unhandled exception');
process.exit(0);
}, 500);
}
@@ -308,6 +323,7 @@ const messageHandlers = {
connect: handleConnect,
queryData: handleQueryData,
runScript: handleRunScript,
runOperation: handleRunOperation,
updateCollection: handleUpdateCollection,
collectionData: handleCollectionData,
loadKeys: handleLoadKeys,
@@ -334,19 +350,19 @@ function start() {
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 120 * 1000) {
console.log('Database connection not alive, exiting');
if (time - lastPing > 40 * 1000) {
logger.info('Database connection not alive, exiting');
process.exit(0);
}
}, 60 * 1000);
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
try {
await handleMessage(message);
} catch (e) {
console.error('Error in DB connection', e);
process.send({ msgtype: 'error', error: e.message });
} catch (err) {
logger.error({ err }, 'Error in DB connection');
process.send({ msgtype: 'error', error: err.message });
}
});
}

View File

@@ -1,21 +1,33 @@
const stableStringify = require('json-stable-stringify');
const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools');
const { extractBoolSettingsValue, extractIntSettingsValue, getLogger } = require('dbgate-tools');
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');
const logger = getLogger('srvconnProcess');
let systemConnection;
let storedConnection;
let lastDatabases = null;
let lastStatus = null;
let lastPing = null;
let afterConnectCallbacks = [];
async function handleRefresh() {
const driver = requireEngineDriver(storedConnection);
try {
const databases = await driver.listDatabases(systemConnection);
let databases = await driver.listDatabases(systemConnection);
if (storedConnection?.allowedDatabases?.trim()) {
const allowedDatabaseList = storedConnection.allowedDatabases
.split('\n')
.map(x => x.trim().toLowerCase())
.filter(x => x);
databases = databases.filter(x => allowedDatabaseList.includes(x.name.toLocaleLowerCase()));
}
if (storedConnection?.allowedDatabasesRegex?.trim()) {
const regex = new RegExp(storedConnection.allowedDatabasesRegex, 'i');
databases = databases.filter(x => regex.test(x.name));
}
setStatusName('ok');
const databasesString = stableStringify(databases);
if (lastDatabases != databasesString) {
@@ -75,28 +87,64 @@ async function handleConnect(connection) {
// console.error(err);
setTimeout(() => process.exit(1), 1000);
}
for (const [resolve] of afterConnectCallbacks) {
resolve();
}
afterConnectCallbacks = [];
}
function waitConnected() {
if (systemConnection) return Promise.resolve();
return new Promise((resolve, reject) => {
afterConnectCallbacks.push([resolve, reject]);
});
}
function handlePing() {
lastPing = new Date().getTime();
}
async function handleCreateDatabase({ name }) {
async function handleDatabaseOp(op, { name }) {
const driver = requireEngineDriver(storedConnection);
systemConnection = await connectUtility(driver, storedConnection, 'app');
console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
if (driver.createDatabase) {
await driver.createDatabase(systemConnection, name);
if (driver[op]) {
await driver[op](systemConnection, name);
} else {
await driver.query(systemConnection, `CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
const dmp = driver.createDumper();
dmp[op](name);
logger.info({ sql: dmp.s }, 'Running script');
await driver.query(systemConnection, dmp.s);
}
await handleRefresh();
}
async function handleDriverDataCore(msgid, callMethod) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
const result = await callMethod(driver);
process.send({ msgtype: 'response', msgid, result });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleServerSummary({ msgid }) {
return handleDriverDataCore(msgid, driver => driver.serverSummary(systemConnection));
}
async function handleSummaryCommand({ msgid, command, row }) {
return handleDriverDataCore(msgid, driver => driver.summaryCommand(systemConnection, command, row));
}
const messageHandlers = {
connect: handleConnect,
ping: handlePing,
createDatabase: handleCreateDatabase,
serverSummary: handleServerSummary,
summaryCommand: handleSummaryCommand,
createDatabase: props => handleDatabaseOp('createDatabase', props),
dropDatabase: props => handleDatabaseOp('dropDatabase', props),
};
async function handleMessage({ msgtype, ...other }) {
@@ -109,11 +157,11 @@ function start() {
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 120 * 1000) {
console.log('Server connection not alive, exiting');
if (time - lastPing > 40 * 1000) {
logger.info('Server connection not alive, exiting');
process.exit(0);
}
}, 60 * 1000);
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;

View File

@@ -1,4 +1,4 @@
const uuidv1 = require('uuid/v1');
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
@@ -10,11 +10,18 @@ const requireEngineDriver = require('../utility/requireEngineDriver');
const { decryptConnection } = require('../utility/crypting');
const connectUtility = require('../utility/connectUtility');
const { handleProcessCommunication } = require('../utility/processComm');
const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools');
const logger = getLogger('sessionProcess');
let systemConnection;
let storedConnection;
let afterConnectCallbacks = [];
// let currentHandlers = [];
let lastPing = null;
let lastActivity = null;
let currentProfiler = null;
let executingScripts = 0;
class TableWriter {
constructor() {
@@ -24,7 +31,7 @@ class TableWriter {
}
initializeFromQuery(structure, resultIndex) {
this.jslid = uuidv1();
this.jslid = crypto.randomUUID();
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
fs.writeFileSync(
this.currentFile,
@@ -101,8 +108,9 @@ class TableWriter {
}
class StreamHandler {
constructor(resultIndexHolder, resolve) {
constructor(resultIndexHolder, resolve, startLine) {
this.recordset = this.recordset.bind(this);
this.startLine = startLine;
this.row = this.row.bind(this);
// this.error = this.error.bind(this);
this.done = this.done.bind(this);
@@ -155,14 +163,21 @@ class StreamHandler {
this.resolve();
}
info(info) {
if (info && info.line != null) {
info = {
...info,
line: this.startLine + info.line,
};
}
process.send({ msgtype: 'info', info });
}
}
function handleStream(driver, resultIndexHolder, sql) {
function handleStream(driver, resultIndexHolder, sqlItem) {
return new Promise((resolve, reject) => {
const handler = new StreamHandler(resultIndexHolder, resolve);
driver.stream(systemConnection, sql, handler);
const start = sqlItem.trimStart || sqlItem.start;
const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line);
driver.stream(systemConnection, sqlItem.text, handler);
});
}
@@ -201,7 +216,38 @@ function waitConnected() {
});
}
async function handleStartProfiler({ jslid }) {
lastActivity = new Date().getTime();
await waitConnected();
const driver = requireEngineDriver(storedConnection);
if (!allowExecuteCustomScript(driver)) {
process.send({ msgtype: 'done' });
return;
}
const writer = new TableWriter();
writer.initializeFromReader(jslid);
currentProfiler = await driver.startProfiler(systemConnection, {
row: data => writer.rowFromReader(data),
});
currentProfiler.writer = writer;
}
async function handleStopProfiler({ jslid }) {
lastActivity = new Date().getTime();
const driver = requireEngineDriver(storedConnection);
currentProfiler.writer.close();
driver.stopProfiler(systemConnection, currentProfiler);
currentProfiler = null;
}
async function handleExecuteQuery({ sql }) {
lastActivity = new Date().getTime();
await waitConnected();
const driver = requireEngineDriver(storedConnection);
@@ -218,20 +264,30 @@ async function handleExecuteQuery({ sql }) {
//process.send({ msgtype: 'error', error: e.message });
}
const resultIndexHolder = {
value: 0,
};
for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('stream'))) {
await handleStream(driver, resultIndexHolder, sqlItem);
// const handler = new StreamHandler(resultIndex);
// const stream = await driver.stream(systemConnection, sqlItem, handler);
// handler.stream = stream;
// resultIndex = handler.resultIndex;
executingScripts++;
try {
const resultIndexHolder = {
value: 0,
};
for (const sqlItem of splitQuery(sql, {
...driver.getQuerySplitterOptions('stream'),
returnRichInfo: true,
})) {
await handleStream(driver, resultIndexHolder, sqlItem);
// const handler = new StreamHandler(resultIndex);
// const stream = await driver.stream(systemConnection, sqlItem, handler);
// handler.stream = stream;
// resultIndex = handler.resultIndex;
}
process.send({ msgtype: 'done' });
} finally {
executingScripts--;
}
process.send({ msgtype: 'done' });
}
async function handleExecuteReader({ jslid, sql, fileName }) {
lastActivity = new Date().getTime();
await waitConnected();
const driver = requireEngineDriver(storedConnection);
@@ -260,10 +316,17 @@ async function handleExecuteReader({ jslid, sql, fileName }) {
});
}
function handlePing() {
lastPing = new Date().getTime();
}
const messageHandlers = {
connect: handleConnect,
executeQuery: handleExecuteQuery,
executeReader: handleExecuteReader,
startProfiler: handleStartProfiler,
stopProfiler: handleStopProfiler,
ping: handlePing,
// cancel: handleCancel,
};
@@ -274,6 +337,35 @@ async function handleMessage({ msgtype, ...other }) {
function start() {
childProcessChecker();
lastPing = new Date().getTime();
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 25 * 1000) {
logger.info('Session not alive, exiting');
process.exit(0);
}
const useSessionTimeout =
storedConnection && storedConnection.globalSettings
? extractBoolSettingsValue(storedConnection.globalSettings, 'session.autoClose', true)
: false;
const sessionTimeout =
storedConnection && storedConnection.globalSettings
? extractIntSettingsValue(storedConnection.globalSettings, 'session.autoCloseTimeout', 15, 1, 120)
: 15;
if (
useSessionTimeout &&
time - lastActivity > sessionTimeout * 60 * 1000 &&
!currentProfiler &&
executingScripts == 0
) {
logger.info('Session not active, exiting');
process.exit(0);
}
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
try {

View File

@@ -3,6 +3,9 @@ const platformInfo = require('../utility/platformInfo');
const childProcessChecker = require('../utility/childProcessChecker');
const { handleProcessCommunication } = require('../utility/processComm');
const { SSHConnection } = require('../utility/SSHConnection');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('sshProcess');
async function getSshConnection(connection) {
const sshConfig = {
@@ -12,10 +15,12 @@ async function getSshConnection(connection) {
agentForward: connection.sshMode == 'agent',
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
username: connection.sshLogin,
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
password: (connection.sshMode || 'userPassword') == 'userPassword' ? connection.sshPassword : undefined,
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
privateKey:
connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
connection.sshMode == 'keyFile' && (connection.sshKeyfile || platformInfo?.defaultKeyfile)
? await fs.readFile(connection.sshKeyfile || platformInfo?.defaultKeyfile)
: undefined,
skipAutoPrivateKey: true,
noReadline: true,
};
@@ -35,8 +40,8 @@ async function handleStart({ connection, tunnelConfig }) {
tunnelConfig,
});
} catch (err) {
console.log('Error creating SSH tunnel connection:', err.message);
logger.error({ err }, 'Error creating SSH tunnel connection:');
process.send({
msgtype: 'error',
connection,

View File

@@ -3,11 +3,14 @@ const fs = require('fs');
const { archivedir, resolveArchiveFolder } = require('../utility/directories');
// const socket = require('../utility/socket');
const jsonLinesWriter = require('./jsonLinesWriter');
const { getLogger } = require('dbgate-tools');
const logger = getLogger();
function archiveWriter({ folderName, fileName }) {
const dir = resolveArchiveFolder(folderName);
if (!fs.existsSync(dir)) {
console.log(`Creating directory ${dir}`);
logger.info(`Creating directory ${dir}`);
fs.mkdirSync(dir);
}
const jsonlFile = path.join(dir, `${fileName}.jsonl`);

View File

@@ -0,0 +1,50 @@
const stream = require('stream');
const path = require('path');
const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const logger = getLogger('dataDuplicator');
const { DataDuplicator } = require('dbgate-datalib');
const copyStream = require('./copyStream');
const jsonLinesReader = require('./jsonLinesReader');
const { resolveArchiveFolder } = require('../utility/directories');
async function dataDuplicator({
connection,
archive,
items,
options,
analysedStructure = null,
driver,
systemConnection,
}) {
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
logger.info(`Connected.`);
if (!analysedStructure) {
analysedStructure = await driver.analyseFull(pool);
}
const dupl = new DataDuplicator(
pool,
driver,
analysedStructure,
items.map(item => ({
name: item.name,
operation: item.operation,
matchColumns: item.matchColumns,
openStream:
item.openStream ||
(() => jsonLinesReader({ fileName: path.join(resolveArchiveFolder(archive), `${item.name}.jsonl`) })),
})),
stream,
copyStream,
options
);
await dupl.run();
}
module.exports = dataDuplicator;

View File

@@ -0,0 +1,16 @@
const importDbModel = require('../utility/importDbModel');
const fs = require('fs');
async function dbModelToJson({ modelFolder, outputFile, commonjs }) {
const dbInfo = await importDbModel(modelFolder);
const json = JSON.stringify(dbInfo, null, 2);
if (commonjs) {
fs.writeFileSync(outputFile, `module.exports = ${json};`);
return;
} else {
fs.writeFileSync(outputFile, json);
}
}
module.exports = dbModelToJson;

View File

@@ -1,11 +1,11 @@
const crypto = require('crypto');
const path = require('path');
const uuidv1 = require('uuid/v1');
const { uploadsdir } = require('../utility/directories');
const { downloadFile } = require('../utility/downloader');
async function download(url) {
if (url && url.match(/(^http:\/\/)|(^https:\/\/)/)) {
const tmpFile = path.join(uploadsdir(), uuidv1());
const tmpFile = path.join(uploadsdir(), crypto.randomUUID());
await downloadFile(url, tmpFile);
return tmpFile;
}

View File

@@ -1,5 +1,8 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('dumpDb');
function doDump(dumper) {
return new Promise((resolve, reject) => {
@@ -21,11 +24,11 @@ async function dumpDatabase({
databaseName,
schemaName,
}) {
console.log(`Dumping database`);
logger.info(`Dumping database`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
console.log(`Connected.`);
logger.info(`Connected.`);
const dumper = await driver.createBackupDumper(pool, {
outputFile,

View File

@@ -1,12 +1,15 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('execQuery');
async function executeQuery({ connection = undefined, systemConnection = undefined, driver = undefined, sql }) {
console.log(`Execute query ${sql}`);
logger.info({ sql }, `Execute query`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'script'));
console.log(`Connected.`);
logger.info(`Connected.`);
await driver.script(pool, sql);
}

View File

@@ -1,18 +1,26 @@
const stream = require('stream');
async function fakeObjectReader({ delay = 0 } = {}) {
async function fakeObjectReader({ delay = 0, dynamicData = null } = {}) {
const pass = new stream.PassThrough({
objectMode: true,
});
function doWrite() {
pass.write({ columns: [{ columnName: 'id' }, { columnName: 'country' }], __isStreamHeader: true });
pass.write({ id: 1, country: 'Czechia' });
pass.write({ id: 2, country: 'Austria' });
pass.write({ country: 'Germany', id: 3 });
pass.write({ country: 'Romania', id: 4 });
pass.write({ country: 'Great Britain', id: 5 });
pass.write({ country: 'Bosna, Hecegovina', id: 6 });
pass.end();
if (dynamicData) {
pass.write({ __isStreamHeader: true, __isDynamicStructure: true });
for (const item of dynamicData) {
pass.write(item);
}
pass.end();
} else {
pass.write({ columns: [{ columnName: 'id' }, { columnName: 'country' }], __isStreamHeader: true });
pass.write({ id: 1, country: 'Czechia' });
pass.write({ id: 2, country: 'Austria' });
pass.write({ country: 'Germany', id: 3 });
pass.write({ country: 'Romania', id: 4 });
pass.write({ country: 'Great Britain', id: 5 });
pass.write({ country: 'Bosna, Hecegovina', id: 6 });
pass.end();
}
}
if (delay) {

View File

@@ -0,0 +1,30 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const {
extendDatabaseInfo,
databaseInfoFromYamlModel,
getAlterDatabaseScript,
DatabaseAnalyser,
} = require('dbgate-tools');
const importDbModel = require('../utility/importDbModel');
const fs = require('fs');
async function generateModelSql({ engine, driver, modelFolder, loadedDbModel, outputFile }) {
if (!driver) driver = requireEngineDriver(engine);
const dbInfo = extendDatabaseInfo(
loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder)
);
const { sql } = getAlterDatabaseScript(
DatabaseAnalyser.createEmptyStructure(),
dbInfo,
{},
DatabaseAnalyser.createEmptyStructure(),
dbInfo,
driver
);
fs.writeFileSync(outputFile, sql);
}
module.exports = generateModelSql;

View File

@@ -4,6 +4,9 @@ const connectUtility = require('../utility/connectUtility');
const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
const download = require('./download');
const stream = require('stream');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('importDb');
class ImportStream extends stream.Transform {
constructor(pool, driver) {
@@ -38,16 +41,16 @@ function awaitStreamEnd(stream) {
}
async function importDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, inputFile }) {
console.log(`Importing database`);
logger.info(`Importing database`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
console.log(`Connected.`);
logger.info(`Connected.`);
const downloadedFile = await download(inputFile);
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions());
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions('script'));
const importStream = new ImportStream(pool, driver);
// @ts-ignore
splittedStream.pipe(importStream);

View File

@@ -23,6 +23,12 @@ const deployDb = require('./deployDb');
const initializeApiEnvironment = require('./initializeApiEnvironment');
const dumpDatabase = require('./dumpDatabase');
const importDatabase = require('./importDatabase');
const loadDatabase = require('./loadDatabase');
const generateModelSql = require('./generateModelSql');
const modifyJsonLinesReader = require('./modifyJsonLinesReader');
const dataDuplicator = require('./dataDuplicator');
const dbModelToJson = require('./dbModelToJson');
const jsonToDbModel = require('./jsonToDbModel');
const dbgateApi = {
queryReader,
@@ -49,6 +55,12 @@ const dbgateApi = {
initializeApiEnvironment,
dumpDatabase,
importDatabase,
loadDatabase,
generateModelSql,
modifyJsonLinesReader,
dataDuplicator,
dbModelToJson,
jsonToDbModel,
};
requirePlugin.initializeDbgateApi(dbgateApi);

View File

@@ -1,6 +1,9 @@
const { getLogger } = require('dbgate-tools');
const fs = require('fs');
const stream = require('stream');
const logger = getLogger('jsonArrayWriter');
class StringifyStream extends stream.Transform {
constructor() {
super({ objectMode: true });
@@ -38,7 +41,7 @@ class StringifyStream extends stream.Transform {
}
async function jsonArrayWriter({ fileName, encoding = 'utf-8' }) {
console.log(`Writing file ${fileName}`);
logger.info(`Writing file ${fileName}`);
const stringify = new StringifyStream();
const fileStream = fs.createWriteStream(fileName, encoding);
stringify.pipe(fileStream);

View File

@@ -1,6 +1,8 @@
const fs = require('fs');
const stream = require('stream');
const byline = require('byline');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('jsonLinesReader');
class ParseStream extends stream.Transform {
constructor({ limitRows }) {
@@ -31,9 +33,13 @@ class ParseStream extends stream.Transform {
}
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
console.log(`Reading file ${fileName}`);
logger.info(`Reading file ${fileName}`);
const fileStream = fs.createReadStream(fileName, encoding);
const fileStream = fs.createReadStream(
fileName,
// @ts-ignore
encoding
);
const liner = byline(fileStream);
const parser = new ParseStream({ limitRows });
liner.pipe(parser);

View File

@@ -1,5 +1,7 @@
const { getLogger } = require('dbgate-tools');
const fs = require('fs');
const stream = require('stream');
const logger = getLogger('jsonLinesWriter');
class StringifyStream extends stream.Transform {
constructor({ header }) {
@@ -10,7 +12,9 @@ class StringifyStream extends stream.Transform {
_transform(chunk, encoding, done) {
let skip = false;
if (!this.wasHeader) {
skip = (chunk.__isStreamHeader && !this.header) || (chunk.__isStreamHeader && chunk.__isDynamicStructure);
skip =
(chunk.__isStreamHeader && !this.header) ||
(chunk.__isStreamHeader && chunk.__isDynamicStructure && !chunk.__keepDynamicStreamHeader);
this.wasHeader = true;
}
if (!skip) {
@@ -21,7 +25,7 @@ class StringifyStream extends stream.Transform {
}
async function jsonLinesWriter({ fileName, encoding = 'utf-8', header = true }) {
console.log(`Writing file ${fileName}`);
logger.info(`Writing file ${fileName}`);
const stringify = new StringifyStream({ header });
const fileStream = fs.createWriteStream(fileName, encoding);
stringify.pipe(fileStream);

View File

@@ -0,0 +1,9 @@
const exportDbModel = require('../utility/exportDbModel');
const fs = require('fs');
async function jsonToDbModel({ modelFile, outputDir }) {
const dbInfo = JSON.parse(fs.readFileSync(modelFile, 'utf-8'));
await exportDbModel(dbInfo, outputDir);
}
module.exports = jsonToDbModel;

View File

@@ -0,0 +1,21 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { getLogger } = require('dbgate-tools');
const exportDbModel = require('../utility/exportDbModel');
const logger = getLogger('analyseDb');
async function loadDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, outputDir }) {
logger.info(`Analysing database`);
if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
logger.info(`Connected.`);
const dbInfo = await driver.analyseFull(pool);
logger.info(`Analyse finished`);
await exportDbModel(dbInfo, outputDir);
}
module.exports = loadDatabase;

View File

@@ -0,0 +1,145 @@
const fs = require('fs');
const _ = require('lodash');
const stream = require('stream');
const byline = require('byline');
const { getLogger, processJsonDataUpdateCommands, removeTablePairingId } = require('dbgate-tools');
const logger = getLogger('modifyJsonLinesReader');
const stableStringify = require('json-stable-stringify');
class ParseStream extends stream.Transform {
constructor({ limitRows, changeSet, mergedRows, mergeKey, mergeMode }) {
super({ objectMode: true });
this.limitRows = limitRows;
this.changeSet = changeSet;
this.wasHeader = false;
this.currentRowIndex = 0;
if (mergeMode == 'merge') {
if (mergedRows && mergeKey) {
this.mergedRowsDict = {};
for (const row of mergedRows) {
const key = stableStringify(_.pick(row, mergeKey));
this.mergedRowsDict[key] = row;
}
}
}
this.mergedRowsArray = mergedRows;
this.mergeKey = mergeKey;
this.mergeMode = mergeMode;
}
_transform(chunk, encoding, done) {
let obj = JSON.parse(chunk);
if (obj.__isStreamHeader) {
if (this.changeSet && this.changeSet.structure) {
this.push({
...removeTablePairingId(this.changeSet.structure),
__isStreamHeader: true,
});
} else {
this.push(obj);
}
this.wasHeader = true;
done();
return;
}
if (this.changeSet) {
if (!this.wasHeader && this.changeSet.structure) {
this.push({
...removeTablePairingId(this.changeSet.structure),
__isStreamHeader: true,
});
this.wasHeader = true;
}
if (!this.limitRows || this.currentRowIndex < this.limitRows) {
if (this.changeSet.deletes.find(x => x.existingRowIndex == this.currentRowIndex)) {
obj = null;
}
const update = this.changeSet.updates.find(x => x.existingRowIndex == this.currentRowIndex);
if (update) {
if (update.document) {
obj = update.document;
} else {
obj = {
...obj,
...update.fields,
};
}
}
if (obj) {
if (this.changeSet.dataUpdateCommands) {
obj = processJsonDataUpdateCommands(obj, this.changeSet.dataUpdateCommands);
}
this.push(obj);
}
this.currentRowIndex += 1;
}
} else if (this.mergedRowsArray && this.mergeKey && this.mergeMode) {
if (this.mergeMode == 'merge') {
const key = stableStringify(_.pick(obj, this.mergeKey));
if (this.mergedRowsDict[key]) {
this.push({ ...obj, ...this.mergedRowsDict[key] });
delete this.mergedRowsDict[key];
} else {
this.push(obj);
}
} else if (this.mergeMode == 'append') {
this.push(obj);
}
} else {
this.push(obj);
}
done();
}
_flush(done) {
if (this.changeSet) {
for (const insert of this.changeSet.inserts) {
this.push({
...insert.document,
...insert.fields,
});
}
} else if (this.mergedRowsArray && this.mergeKey) {
if (this.mergeMode == 'merge') {
for (const row of this.mergedRowsArray) {
const key = stableStringify(_.pick(row, this.mergeKey));
if (this.mergedRowsDict[key]) {
this.push(row);
}
}
} else {
for (const row of this.mergedRowsArray) {
this.push(row);
}
}
}
done();
}
}
async function modifyJsonLinesReader({
fileName,
encoding = 'utf-8',
limitRows = undefined,
changeSet = null,
mergedRows = null,
mergeKey = null,
mergeMode = 'merge',
}) {
logger.info(`Reading file ${fileName} with change set`);
const fileStream = fs.createReadStream(
fileName,
// @ts-ignore
encoding
);
const liner = byline(fileStream);
const parser = new ParseStream({ limitRows, changeSet, mergedRows, mergeKey, mergeMode });
liner.pipe(parser);
return parser;
}
module.exports = modifyJsonLinesReader;

View File

@@ -1,5 +1,7 @@
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('queryReader');
async function queryReader({
connection,
@@ -14,13 +16,15 @@ async function queryReader({
// if (!sql && !json) {
// throw new Error('One of sql or json must be set');
// }
console.log(`Reading query ${query || sql}`);
logger.info({ sql: query || sql }, `Reading query`);
// else console.log(`Reading query ${JSON.stringify(json)}`);
const driver = requireEngineDriver(connection);
const pool = await connectUtility(driver, connection, queryType == 'json' ? 'read' : 'script');
console.log(`Connected.`);
return queryType == 'json' ? await driver.readJsonQuery(pool, query) : await driver.readQuery(pool, query || sql);
logger.info(`Connected.`);
const reader =
queryType == 'json' ? await driver.readJsonQuery(pool, query) : await driver.readQuery(pool, query || sql);
return reader;
}
module.exports = queryReader;

View File

@@ -3,12 +3,17 @@ const fs = require('fs');
const { pluginsdir, packagedPluginsDir, getPluginBackendPath } = require('../utility/directories');
const nativeModules = require('../nativeModules');
const platformInfo = require('../utility/platformInfo');
const authProxy = require('../utility/authProxy');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('requirePlugin');
const loadedPlugins = {};
const dbgateEnv = {
dbgateApi: null,
nativeModules,
platformInfo,
authProxy,
};
function requirePlugin(packageName, requiredPlugin = null) {
if (!packageName) throw new Error('Missing packageName in plugin');
@@ -17,7 +22,7 @@ function requirePlugin(packageName, requiredPlugin = null) {
if (requiredPlugin == null) {
let module;
const modulePath = getPluginBackendPath(packageName);
console.log(`Loading module ${packageName} from ${modulePath}`);
logger.info(`Loading module ${packageName} from ${modulePath}`);
try {
// @ts-ignore
module = __non_webpack_require__(modulePath);

View File

@@ -1,5 +1,7 @@
const { getLogger } = require('dbgate-tools');
const childProcessChecker = require('../utility/childProcessChecker');
const processArgs = require('../utility/processArgs');
const logger = getLogger();
async function runScript(func) {
if (processArgs.checkParent) {
@@ -9,7 +11,7 @@ async function runScript(func) {
await func();
process.exit(0);
} catch (err) {
console.log(err);
logger.error({ err }, `Error running script: ${err.message}`);
process.exit(1);
}
}

View File

@@ -1,8 +1,9 @@
const fs = require('fs');
const stream = require('stream');
const path = require('path');
const { driverBase } = require('dbgate-tools');
const { driverBase, getLogger } = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const logger = getLogger('sqlDataWriter');
class SqlizeStream extends stream.Transform {
constructor({ fileName, dataName }) {
@@ -40,7 +41,7 @@ class SqlizeStream extends stream.Transform {
}
async function sqlDataWriter({ fileName, dataName, driver, encoding = 'utf-8' }) {
console.log(`Writing file ${fileName}`);
logger.info(`Writing file ${fileName}`);
const stringify = new SqlizeStream({ fileName, dataName });
const fileStream = fs.createWriteStream(fileName, encoding);
stringify.pipe(fileStream);

View File

@@ -1,17 +1,18 @@
const { quoteFullName, fullNameToString } = require('dbgate-tools');
const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const logger = getLogger('tableReader');
async function tableReader({ connection, pureName, schemaName }) {
const driver = requireEngineDriver(connection);
const pool = await connectUtility(driver, connection, 'read');
console.log(`Connected.`);
logger.info(`Connected.`);
const fullName = { pureName, schemaName };
if (driver.databaseEngineTypes.includes('document')) {
// @ts-ignore
console.log(`Reading collection ${fullNameToString(fullName)}`);
logger.info(`Reading collection ${fullNameToString(fullName)}`);
// @ts-ignore
return await driver.readQuery(pool, JSON.stringify(fullName));
}
@@ -20,14 +21,14 @@ async function tableReader({ connection, pureName, schemaName }) {
const query = `select * from ${quoteFullName(driver.dialect, fullName)}`;
if (table) {
// @ts-ignore
console.log(`Reading table ${fullNameToString(table)}`);
logger.info(`Reading table ${fullNameToString(table)}`);
// @ts-ignore
return await driver.readQuery(pool, query, table);
}
const view = await driver.analyseSingleObject(pool, fullName, 'views');
if (view) {
// @ts-ignore
console.log(`Reading view ${fullNameToString(view)}`);
logger.info(`Reading view ${fullNameToString(view)}`);
// @ts-ignore
return await driver.readQuery(pool, query, view);
}

View File

@@ -1,16 +1,17 @@
const { fullNameToString } = require('dbgate-tools');
const { fullNameToString, getLogger } = require('dbgate-tools');
const requireEngineDriver = require('../utility/requireEngineDriver');
const connectUtility = require('../utility/connectUtility');
const logger = getLogger('tableWriter');
async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
console.log(`Writing table ${fullNameToString({ schemaName, pureName })}`);
logger.info(`Writing table ${fullNameToString({ schemaName, pureName })}`);
if (!driver) {
driver = requireEngineDriver(connection);
}
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
console.log(`Connected.`);
logger.info(`Connected.`);
return await driver.writeTable(pool, { schemaName, pureName }, options);
}

View File

@@ -1,7 +1,10 @@
const crypto = require('crypto');
const { fork } = require('child_process');
const uuidv1 = require('uuid/v1');
const { handleProcessCommunication } = require('./processComm');
const processArgs = require('../utility/processArgs');
const pipeForkLogs = require('./pipeForkLogs');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('DatastoreProxy');
class DatastoreProxy {
constructor(file) {
@@ -30,13 +33,20 @@ class DatastoreProxy {
async ensureSubprocess() {
if (!this.subprocess) {
this.subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
'--is-forked-api',
'--start-process',
'jslDatastoreProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
]);
this.subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
'--is-forked-api',
'--start-process',
'jslDatastoreProcess',
...processArgs.getPassArgs(),
// ...process.argv.slice(3),
],
{
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
pipeForkLogs(this.subprocess);
this.subprocess.on('message', message => {
// @ts-ignore
@@ -57,19 +67,29 @@ class DatastoreProxy {
async getRows(offset, limit) {
await this.ensureSubprocess();
const msgid = uuidv1();
const msgid = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
this.requests[msgid] = [resolve, reject];
this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
try {
this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
} catch (err) {
logger.error({ err }, 'Error getting rows');
this.subprocess = null;
}
});
return promise;
}
async notifyChangedCore() {
const msgid = uuidv1();
const msgid = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
this.requests[msgid] = [resolve, reject];
this.subprocess.send({ msgtype: 'notify', msgid });
try {
this.subprocess.send({ msgtype: 'notify', msgid });
} catch (err) {
logger.error({ err }, 'Error notifying subprocess');
this.subprocess = null;
}
});
return promise;
}

View File

@@ -1,6 +1,6 @@
const crypto = require('crypto');
const AsyncLock = require('async-lock');
const fs = require('fs-extra');
const uuidv1 = require('uuid/v1');
const lock = new AsyncLock();
@@ -57,7 +57,7 @@ class JsonLinesDatabase {
? obj
: {
...obj,
_id: uuidv1(),
_id: crypto.randomUUID(),
};
this.data.push(elem);
await this._save();
@@ -90,6 +90,12 @@ class JsonLinesDatabase {
return obj;
}
async updateAll(mapFunction) {
await this._ensureLoaded();
this.data = this.data.map(mapFunction);
await this._save();
}
async patch(id, values) {
await this._ensureLoaded();
this.data = this.data.map(x => (x._id == id ? { ...x, ...values } : x));

View File

@@ -1,38 +1,64 @@
const lineReader = require('line-reader');
const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const rimraf = require('rimraf');
const path = require('path');
const AsyncLock = require('async-lock');
const lock = new AsyncLock();
const stableStringify = require('json-stable-stringify');
const { evaluateCondition } = require('dbgate-sqltree');
function fetchNextLineFromReader(reader) {
return new Promise((resolve, reject) => {
if (!reader.hasNextLine()) {
resolve(null);
return;
}
reader.nextLine((err, line) => {
if (err) {
reject(err);
} else {
resolve(line);
}
});
});
}
const requirePluginFunction = require('./requirePluginFunction');
const esort = require('external-sorting');
const { jsldir } = require('./directories');
const LineReader = require('./LineReader');
class JsonLinesDatastore {
constructor(file) {
constructor(file, formatterFunction) {
this.file = file;
this.formatterFunction = formatterFunction;
this.reader = null;
this.readedDataRowCount = 0;
this.readedSchemaRow = false;
// this.firstRowToBeReturned = null;
this.notifyChangedCallback = null;
this.currentFilter = null;
this.currentSort = null;
this.rowFormatter = requirePluginFunction(formatterFunction);
this.sortedFiles = {};
}
_closeReader() {
static async sortFile(infile, outfile, sort) {
const tempDir = path.join(os.tmpdir(), crypto.randomUUID());
fs.mkdirSync(tempDir);
await esort
.default({
input: fs.createReadStream(infile),
output: fs.createWriteStream(outfile),
deserializer: JSON.parse,
serializer: JSON.stringify,
tempDir,
maxHeap: 100,
comparer: (a, b) => {
for (const item of sort) {
const { uniqueName, order } = item;
if (a[uniqueName] < b[uniqueName]) {
return order == 'ASC' ? -1 : 1;
}
if (a[uniqueName] > b[uniqueName]) {
return order == 'ASC' ? 1 : -1;
}
}
return 0;
},
})
.asc();
await new Promise(resolve => rimraf(tempDir, resolve));
}
async _closeReader() {
// console.log('CLOSING READER', this.reader);
if (!this.reader) return;
const reader = this.reader;
this.reader = null;
@@ -40,7 +66,8 @@ class JsonLinesDatastore {
this.readedSchemaRow = false;
// this.firstRowToBeReturned = null;
this.currentFilter = null;
reader.close(() => {});
this.currentSort = null;
await reader.close();
}
async notifyChanged(callback) {
@@ -53,13 +80,17 @@ class JsonLinesDatastore {
if (call) call();
}
async _openReader() {
return new Promise((resolve, reject) =>
lineReader.open(this.file, (err, reader) => {
if (err) reject(err);
resolve(reader);
})
);
async _openReader(fileName) {
// console.log('OPENING READER', fileName);
// console.log(fs.readFileSync(fileName, 'utf-8'));
const fileStream = fs.createReadStream(fileName);
return new LineReader(fileStream);
}
parseLine(line) {
const res = JSON.parse(line);
return this.rowFormatter ? this.rowFormatter(res) : res;
}
async _readLine(parse) {
@@ -69,7 +100,7 @@ class JsonLinesDatastore {
// return res;
// }
for (;;) {
const line = await fetchNextLineFromReader(this.reader);
const line = await this.reader.readLine();
if (!line) {
// EOF
return null;
@@ -84,14 +115,14 @@ class JsonLinesDatastore {
}
}
if (this.currentFilter) {
const parsedLine = JSON.parse(line);
const parsedLine = this.parseLine(line);
if (evaluateCondition(this.currentFilter, parsedLine)) {
this.readedDataRowCount += 1;
return parse ? parsedLine : true;
}
} else {
this.readedDataRowCount += 1;
return parse ? JSON.parse(line) : true;
return parse ? this.parseLine(line) : true;
}
}
@@ -132,14 +163,19 @@ class JsonLinesDatastore {
// });
}
async _ensureReader(offset, filter) {
if (this.readedDataRowCount > offset || stableStringify(filter) != stableStringify(this.currentFilter)) {
async _ensureReader(offset, filter, sort) {
if (
this.readedDataRowCount > offset ||
stableStringify(filter) != stableStringify(this.currentFilter) ||
stableStringify(sort) != stableStringify(this.currentSort)
) {
this._closeReader();
}
if (!this.reader) {
const reader = await this._openReader();
const reader = await this._openReader(sort ? this.sortedFiles[stableStringify(sort)] : this.file);
this.reader = reader;
this.currentFilter = filter;
this.currentSort = sort;
}
// if (!this.readedSchemaRow) {
// const line = await this._readLine(true); // skip structure
@@ -171,13 +207,20 @@ class JsonLinesDatastore {
});
}
async getRows(offset, limit, filter) {
async getRows(offset, limit, filter, sort) {
const res = [];
if (sort && !this.sortedFiles[stableStringify(sort)]) {
const jslid = crypto.randomUUID();
const sortedFile = path.join(jsldir(), `${jslid}.jsonl`);
await JsonLinesDatastore.sortFile(this.file, sortedFile, sort);
this.sortedFiles[stableStringify(sort)] = sortedFile;
}
await lock.acquire('reader', async () => {
await this._ensureReader(offset, filter);
await this._ensureReader(offset, filter, sort);
// console.log(JSON.stringify(this.currentFilter, undefined, 2));
for (let i = 0; i < limit; i += 1) {
const line = await this._readLine(true);
// console.log('READED LINE', i);
if (line == null) break;
res.push(line);
}

View File

@@ -0,0 +1,88 @@
const readline = require('readline');
class Queue {
constructor() {
this.elements = {};
this.head = 0;
this.tail = 0;
}
enqueue(element) {
this.elements[this.tail] = element;
this.tail++;
}
dequeue() {
const item = this.elements[this.head];
delete this.elements[this.head];
this.head++;
return item;
}
peek() {
return this.elements[this.head];
}
getLength() {
return this.tail - this.head;
}
isEmpty() {
return this.getLength() === 0;
}
}
class LineReader {
constructor(input) {
this.input = input;
this.queue = new Queue();
this.resolve = null;
this.isEnded = false;
this.rl = readline.createInterface({
input,
});
this.input.pause();
this.rl.on('line', line => {
this.input.pause();
if (this.resolve) {
const resolve = this.resolve;
this.resolve = null;
resolve(line);
return;
}
this.queue.enqueue(line);
});
this.rl.on('close', () => {
if (this.resolve) {
const resolve = this.resolve;
this.resolve = null;
this.isEnded = true;
resolve(null);
return;
}
this.queue.enqueue(null);
});
}
readLine() {
if (this.isEnded) {
return Promise.resolve(null);
}
if (!this.queue.isEmpty()) {
const res = this.queue.dequeue();
if (res == null) this.isEnded = true;
return Promise.resolve(res);
}
this.input.resume();
return new Promise(resolve => {
this.resolve = resolve;
});
}
close() {
this.isEnded = true;
return new Promise(resolve => this.input.close(resolve));
}
}
module.exports = LineReader;

View File

@@ -0,0 +1,20 @@
function isAuthProxySupported() {
return false;
}
async function authProxyGetRedirectUrl(options) {
return null;
}
async function authProxyGetTokenFromCode(options) {
return null;
}
function startTokenChecking(sid, callback) {}
module.exports = {
isAuthProxySupported,
authProxyGetRedirectUrl,
authProxyGetTokenFromCode,
startTokenChecking,
};

View File

@@ -0,0 +1,10 @@
function checkLicense() {
return {
status: 'ok',
type: 'community',
};
}
module.exports = {
checkLicense,
};

View File

@@ -1,14 +1,18 @@
const { getLogger } = require('dbgate-tools');
const logger = getLogger('childProcessChecked');
let counter = 0;
function childProcessChecker() {
setInterval(() => {
try {
process.send({ msgtype: 'ping', counter: counter++ });
} catch (ex) {
} catch (err) {
// This will come once parent dies.
// One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
// and call process.exit()
console.log('parent died', ex.toString());
logger.error({ err }, 'parent died');
process.exit(1);
}
}, 1000);

View File

@@ -2,7 +2,7 @@ const fs = require('fs-extra');
const path = require('path');
const ageSeconds = 3600;
async function cleanDirectory(directory) {
async function cleanDirectory(directory, age = undefined) {
const files = await fs.readdir(directory);
const now = new Date().getTime();
@@ -10,7 +10,7 @@ async function cleanDirectory(directory) {
const full = path.join(directory, file);
const stat = await fs.stat(full);
const mtime = stat.mtime.getTime();
const expirationTime = mtime + ageSeconds * 1000;
const expirationTime = mtime + (age || ageSeconds) * 1000;
if (now > expirationTime) {
if (stat.isDirectory()) {
await fs.rmdir(full, { recursive: true });

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