Compare commits

..

436 Commits

Author SHA1 Message Date
Stela Augustinova a3d9fe76d6 Merge branch 'master' into feature/postgresql-export-bytea 2025-11-13 13:44:11 +01:00
Stela Augustinova 0f4f154637 Add support for base64 image source from binary object 2025-11-13 13:37:33 +01:00
Stela Augustinova 7d112a208f Enhance binary data handling in modifyRow function to support ArrayBuffer conversion to base64 2025-11-13 13:12:51 +01:00
Stela Augustinova c867d39d8d Remove recordset call in MySQL driver 2025-11-13 10:26:12 +01:00
Stela Augustinova 83b6c939f7 Test binary - added engine label 2025-11-13 10:10:33 +01:00
Stela Augustinova e1e4eb5d6f Added binary data types for engines 2025-11-13 09:40:05 +01:00
Stela Augustinova a14c08f122 Handle binary data in load cell from file by converting Buffer to base64 2025-11-12 17:03:35 +01:00
SPRINX0\prochazka 5fd50dcf45 SYNC: fix - license in pro widget 2025-11-12 15:40:48 +00:00
SPRINX0\prochazka b2ac4ee245 SYNC: CSV parameters 2025-11-12 10:22:12 +00:00
SPRINX0\prochazka 0ad7c0546b CSV for MS Excel export 2025-11-12 10:59:50 +01:00
SPRINX0\prochazka 748381fef3 v6.6.12-premium-beta.5 2025-11-12 08:50:46 +01:00
SPRINX0\prochazka 7eb9e42210 SYNC: load pro widget in trial 2025-11-12 07:47:06 +00:00
SPRINX0\prochazka 36a4b67ef4 SYNC: promo widget in trial 2025-11-12 07:47:05 +00:00
Stela Augustinova 94b35e3d5f Enhance binary data handling by converting hex strings to base64 in filter parser and updating MongoDB driver for BinData support 2025-11-11 14:39:43 +01:00
Jan Prochazka c5aa82b76a Merge pull request #1252 from dbgate/feature/translation2
Feature/translation2
2025-11-11 08:50:22 +01:00
SPRINX0\prochazka c2920e195f optimalization 2025-11-11 08:25:56 +01:00
SPRINX0\prochazka 92a78a419e fix in translation 2025-11-11 08:17:42 +01:00
Stela Augustinova 4f27c2b852 translation-select language 2025-11-10 16:29:36 +01:00
Jan Prochazka ece5779b41 Merge pull request #1251 from dbgate/feature/electron-upgrade
Feature/electron upgrade
2025-11-10 16:26:48 +01:00
Stela Augustinova 167aaa8491 translation-commands 2025-11-10 16:14:32 +01:00
SPRINX0\prochazka de622b055b v6.6.12-premium-beta.3 2025-11-10 16:00:04 +01:00
SPRINX0\prochazka 7e88b930ec v6.6.12-beta.2 2025-11-10 15:25:51 +01:00
SPRINX0\prochazka c9adc2b852 update packages 2025-11-10 15:25:39 +01:00
SPRINX0\prochazka 91d3aba611 v6.6.12-beta.1 2025-11-10 15:12:03 +01:00
SPRINX0\prochazka 48bc11dcb5 try to upgrade electron #1243 2025-11-10 15:11:45 +01:00
Stela Augustinova 9ee0b32cac translation-db object 2025-11-10 14:42:20 +01:00
SPRINX0\prochazka 4b6b74604b Use SSL automatically for Azure SQL 2025-11-10 11:57:48 +01:00
SPRINX0\prochazka 7ad3fc4751 SYNC: changelog 2025-11-07 16:03:27 +00:00
SPRINX0\prochazka e604450cfb v6.6.11 2025-11-07 17:01:21 +01:00
SPRINX0\prochazka 9cfbc83896 v6.6.11-premium-beta.3 2025-11-07 16:30:25 +01:00
SPRINX0\prochazka 98b2f4ec35 v6.6.11-premium-beta.2 2025-11-07 16:29:51 +01:00
SPRINX0\prochazka 47e30f2daf v6.6.11-beta.1 2025-11-07 16:29:51 +01:00
SPRINX0\prochazka 6c21da6959 SYNC: themable starting screen 2025-11-07 15:28:49 +00:00
SPRINX0\prochazka c5fe71d390 SYNC: text 2025-11-07 15:04:39 +00:00
Stela Augustinova d14ffcb736 translation-data form,data grid 2025-11-07 14:49:00 +01:00
Stela Augustinova e694aca70b Refactor to use _val for translation handling across components 2025-11-07 14:40:52 +01:00
SPRINX0\prochazka 3c58cb1b9c SYNC: licensing page improvements 2025-11-07 13:33:01 +00:00
SPRINX0\prochazka dc452cdadf SYNC: disable commands on special pages 2025-11-07 12:10:13 +00:00
SPRINX0\prochazka e855365cbb SYNC: themable special page layout 2025-11-07 11:19:03 +00:00
SPRINX0\prochazka cffa288227 SYNC: fix - dont' show devtools 2025-11-07 09:26:26 +00:00
SPRINX0\prochazka 89ddced342 SYNC: disable commands when modal is opened 2025-11-07 09:25:18 +00:00
Jan Prochazka 7457328b59 v6.6.10 2025-11-06 22:34:02 +01:00
Jan Prochazka 44ff413810 changelog 2025-11-06 22:33:29 +01:00
SPRINX0\prochazka d174c2c2d8 v6.6.10-premium-beta.2 2025-11-06 16:44:43 +01:00
SPRINX0\prochazka 480f4c9a8c v6.6.10-beta.1 2025-11-06 16:44:35 +01:00
SPRINX0\prochazka 7474cc5d8a SYNC: fixed trial scenario 2025-11-06 15:43:56 +00:00
CI workflows bda5c4f5dd chore: auto-update github workflows 2025-11-06 14:37:33 +00:00
CI workflows 4f5034c167 Update pro ref 2025-11-06 14:37:14 +00:00
SPRINX0\prochazka 9470db1f4d SYNC: fixed License fron environment variable is not refreshed #1245 2025-11-06 14:37:04 +00:00
Stela Augustinova dfeb910ac9 Enhance binary data handling by integrating modifyRow function in SQLite driver and helpers 2025-11-06 14:14:24 +01:00
SPRINX0\prochazka a5745795be SYNC: command palette fix 2025-11-06 12:43:04 +00:00
Stela Augustinova 98f2b5dd08 Enhance binary data handling in Oracle driver and adjust dumper for byte array values 2025-11-06 13:08:50 +01:00
SPRINX0\prochazka f642c7570e SYNC: fixed translating app object list 2025-11-06 12:07:54 +00:00
Stela Augustinova dca9ea24d7 Implement base64 encoding for binary data in SQL dumper and modify row handling in MySQL and MSSQL drivers 2025-11-06 12:30:48 +01:00
SPRINX0\prochazka 43fd7b6000 rowsAffected field added 2025-11-06 08:23:31 +01:00
Jan Prochazka 1553ec3bd4 Query result - added click-throught to returned data #1236 2025-11-05 18:35:33 +01:00
Stela Augustinova 37d54811e0 Enhance binary data handling in transformRow to serialize Buffer as base64 2025-11-05 16:07:14 +01:00
Stela Augustinova 0f2af6eb37 Add binary data handling in query tests with enhanced stream handler 2025-11-05 14:09:58 +01:00
SPRINX0\prochazka 91546228fa changed query workflow 2025-11-05 13:34:50 +01:00
SPRINX0\prochazka 5488ff06e0 _t 2025-11-05 13:34:50 +01:00
SPRINX0\prochazka ef7f050bc5 Show executed query on log #1236 2025-11-05 13:34:50 +01:00
SPRINX0\prochazka e5c94d9698 Merge branch 'feature/translation' 2025-11-05 12:18:23 +01:00
Stela Augustinova 4d3e0ac5d9 Merge branch 'master' into feature/translation 2025-11-05 11:56:51 +01:00
Stela Augustinova 0a2f0372be Enhance translation regex to support multiple default message formats 2025-11-05 11:49:21 +01:00
Stela Augustinova e378fc3cfb translation-table editor, query, table structure 2025-11-04 16:52:45 +01:00
SPRINX0\prochazka 75a1d74d9c handle changed current database for MS SQL #1237 2025-11-04 15:39:37 +01:00
SPRINX0\prochazka 1404685296 changing current DB handler for MySQL #1237 2025-11-04 15:20:21 +01:00
Stela Augustinova 0ebed9b46f Refactor dataCopy to avoid mutating original structure 2025-11-04 12:32:20 +01:00
SPRINX0\prochazka af69352361 SYNC: fixed references error 2025-11-04 08:09:30 +00:00
Jan Prochazka 840e3b861f fixed horizontal grid scroll on MacOS #453 2025-11-04 08:29:54 +01:00
Jan Prochazka a7cfe7fe04 fixed possible render error 2025-11-04 08:27:36 +01:00
SPRINX0\prochazka a324cf0fcd Column name collision resolving #1234 for postgres - commented out (not working) 2025-11-03 16:56:58 +01:00
SPRINX0\prochazka 0919f4c85b FEAT: column name collision resolving #1234 (MySQL)) 2025-11-03 16:30:56 +01:00
SPRINX0\prochazka fd4cc6a1e8 #1235 ability to load CSV files 2025-11-03 15:44:57 +01:00
Stela Augustinova d6ae3d4f16 Refactor binary data handling in SQL dumper and update test for binary insertion 2025-11-03 13:31:43 +01:00
Stela Augustinova 9c1819467a test binary data type 2025-11-03 09:17:11 +01:00
SPRINX0\prochazka 08410ef209 v6.6.9 2025-10-31 16:32:37 +01:00
SPRINX0\prochazka 6cd3242454 v6.6.9-premium-beta.1 2025-10-31 16:05:43 +01:00
CI workflows 986c1a7bc8 chore: auto-update github workflows 2025-10-31 15:05:12 +00:00
SPRINX0\prochazka a7703ec996 fix build 2025-10-31 16:04:46 +01:00
SPRINX0\prochazka 52d2eb3f59 v6.6.8 2025-10-31 15:22:13 +01:00
SPRINX0\prochazka b19fed41ef changelog 2025-10-31 15:20:51 +01:00
Stela Augustinova 82a4b2c769 translation-clipboard, exportMenu, macros, datagrid commands 2025-10-31 15:10:17 +01:00
SPRINX0\prochazka 1d52cde3b3 v6.6.8-beta.19 2025-10-31 14:38:07 +01:00
SPRINX0\prochazka aca4cc0ace fix 2025-10-31 14:37:57 +01:00
SPRINX0\prochazka 8f9a78feb9 v6.6.8-beta.18 2025-10-31 14:01:57 +01:00
SPRINX0\prochazka 45484a43f1 v6.6.8-beta.17 2025-10-31 14:01:32 +01:00
CI workflows 63665e6e9c chore: auto-update github workflows 2025-10-31 13:00:53 +00:00
SPRINX0\prochazka 151665c880 pragmatic hack - fix yaml hashes for windows 2025-10-31 14:00:23 +01:00
SPRINX0\prochazka 15e475873e v6.8.8-beta.14 2025-10-31 10:49:37 +01:00
SPRINX0\prochazka f846af75e8 Merge branch 'moveprem' 2025-10-31 10:47:34 +01:00
SPRINX0\prochazka 5c7ab3793f v6.6.8-beta.13 2025-10-31 08:21:42 +01:00
CI workflows a501f0cdef chore: auto-update github workflows 2025-10-31 07:19:48 +00:00
SPRINX0\prochazka 4b4c2606cd skipped publish 2025-10-31 08:19:15 +01:00
SPRINX0\prochazka 901e7581fe v6.6.8-alpha.10 2025-10-30 14:32:16 +01:00
SPRINX0\prochazka 55fb233ce4 v6.6.8-alpha.9 2025-10-30 14:20:14 +01:00
SPRINX0\prochazka dd77ee4a8e added missing repo info 2025-10-30 14:18:35 +01:00
CI workflows a7ae6d7b47 chore: auto-update github workflows 2025-10-30 13:18:30 +00:00
CI workflows 34dfac8bb2 Update pro ref 2025-10-30 13:18:11 +00:00
SPRINX0\prochazka 599f783c04 v6.6.8-alpha.8 2025-10-30 14:10:07 +01:00
SPRINX0\prochazka c7f82d3d46 v6.6.8-alpha.7 2025-10-30 14:09:37 +01:00
CI workflows 8b346c6b44 chore: auto-update github workflows 2025-10-30 13:09:06 +00:00
SPRINX0\prochazka e386764151 npm publish fix 2025-10-30 14:08:48 +01:00
SPRINX0\prochazka c0acecc6e9 git repo config 2025-10-30 13:48:44 +01:00
SPRINX0\prochazka a1724134ec v6.6.8-alpha.6 2025-10-30 13:41:53 +01:00
SPRINX0\prochazka 530b830586 fixed repo 2025-10-30 13:41:24 +01:00
SPRINX0\prochazka 363a72c3ad v6.6.8-alpha.5 2025-10-30 13:31:42 +01:00
CI workflows c1a6daf9d2 chore: auto-update github workflows 2025-10-30 12:31:14 +00:00
SPRINX0\prochazka 8a7a73678c added npm publish --tag 2025-10-30 13:30:56 +01:00
SPRINX0\prochazka 6cc8c7cb9d v6.6.8-alpha.4 2025-10-30 13:20:32 +01:00
CI workflows e2d1771a7a chore: auto-update github workflows 2025-10-30 12:18:26 +00:00
SPRINX0\prochazka 23f7dd6ee3 use OIDC NPM sign 2025-10-30 13:18:05 +01:00
SPRINX0\prochazka 9884ace309 fixed translations set during module startup - use __t 2025-10-30 10:37:06 +01:00
Stela Augustinova 70284ac440 translation-datagrid,filter,macros,newObject 2025-10-30 10:12:32 +01:00
SPRINX0\prochazka f24caad997 v6.6.8-beta.1 2025-10-30 08:49:48 +01:00
CI workflows 32729350f6 chore: auto-update github workflows 2025-10-30 07:48:22 +00:00
SPRINX0\prochazka 4929d190a5 azure code signing 2025-10-30 08:47:55 +01:00
Stela Augustinova 0e211dc91b translation-connections,sqlObject,column,filter 2025-10-29 16:55:27 +01:00
Stela Augustinova 8663ab2d28 translation-settings,common 2025-10-29 16:55:27 +01:00
SPRINX0\prochazka ba0ecaf70f fix 2025-10-29 16:47:52 +01:00
SPRINX0\prochazka acb4f5924e upgrade button 2025-10-29 16:43:28 +01:00
SPRINX0\prochazka 46553a80ad v6.6.7 2025-10-29 15:20:51 +01:00
Stela Augustinova 417334d140 Add base64 handling for binary data in filter and grid components 2025-10-29 15:08:57 +01:00
SPRINX0\prochazka e254657813 remvoed links to dbgate.org 2025-10-29 15:06:30 +01:00
SPRINX0\prochazka b087df8d97 v6.6.7-beta.2 2025-10-29 14:59:28 +01:00
SPRINX0\prochazka 47eb74d5ba fix 2025-10-29 14:59:14 +01:00
SPRINX0\prochazka f0ac047978 v6.6.7-beta.1 2025-10-29 14:26:05 +01:00
SPRINX0\prochazka e57e246991 SYNC: fixed web info 2025-10-29 13:00:17 +00:00
SPRINX0\prochazka 6934cdd122 json UI improvements 2025-10-29 13:08:37 +01:00
Stela Augustinova aa7fb74312 PostgreSQL export to SQL and XML bytea contents #1228 2025-10-29 12:22:13 +01:00
SPRINX0\prochazka 0e2a77ced7 changelog 2025-10-27 17:19:16 +01:00
SPRINX0\prochazka 2d135d708e v6.6.6 2025-10-27 17:18:48 +01:00
SPRINX0\prochazka 26d34f896b changelog 2025-10-27 16:20:27 +01:00
SPRINX0\prochazka 87c58cae83 SYNC: fixed test 2025-10-27 14:46:01 +00:00
Stela Augustinova 8429067ae5 translation-connections,sqlObject,column,filter 2025-10-27 15:04:04 +01:00
SPRINX0\prochazka 4d6957a6fa v6.6.6-premium-beta.18 2025-10-27 13:19:40 +01:00
SPRINX0\prochazka 70c53248ae v6.6.6-beta.17 2025-10-27 13:19:20 +01:00
SPRINX0\prochazka 6e645cb054 v6.6.6-beta.7 2025-10-27 13:19:11 +01:00
SPRINX0\prochazka 28fecc6834 v6.6.6-beta.14 2025-10-27 13:18:55 +01:00
SPRINX0\prochazka 64c2faf538 JSON UI 2025-10-27 13:10:45 +01:00
Stela Augustinova c514a4d503 translation-settings,common 2025-10-27 09:33:26 +01:00
Jan Prochazka 391d04b45c MPR archive 2025-10-26 18:28:35 +01:00
Jan Prochazka f974c00a63 MPR references 2025-10-26 18:23:07 +01:00
Jan Prochazka 9f0107c002 MPR refs, macros 2025-10-26 18:06:45 +01:00
Jan Prochazka 6ce82e915e MPR grouping 2025-10-26 15:04:13 +01:00
Jan Prochazka 864797fc99 MPR advanced exports 2025-10-26 09:33:48 +01:00
SPRINX0\prochazka c741434e3c v6.6.6-beta.13 2025-10-23 07:44:45 +02:00
Jan Prochazka 60fcb1a862 Merge pull request #1230 from dbgate/feature/disable-filter
Feature/disable filter
2025-10-22 14:42:51 +02:00
Stela Augustinova 18dc6a3ff5 Allow disable/re-enable multicolumn filter #1174 2025-10-22 14:20:23 +02:00
Stela Augustinova da52dd006b Allow disable/re-enable filter #1174 2025-10-22 14:06:31 +02:00
SPRINX0\prochazka ed1655ed8f better colored icon 2025-10-22 13:56:46 +02:00
SPRINX0\prochazka 516c4e32be v6.6.6-premium-beta.9 2025-10-22 12:40:58 +02:00
SPRINX0\prochazka 1e222d806e SYNC: fixed export from team files 2025-10-22 10:40:37 +00:00
SPRINX0\prochazka bfb35b198d v6.6.6-premium-beta.8 2025-10-22 12:28:03 +02:00
SPRINX0\prochazka 3a5f36155f promo widget colors 2025-10-22 10:47:59 +02:00
SPRINX0\prochazka 82092fab76 premium icon 2025-10-22 10:15:27 +02:00
SPRINX0\prochazka a432cb886d #1211 - fixed incorrect behaviour in server connection ping 2025-10-21 16:34:40 +02:00
Jan Prochazka 5fff8c6ba2 Merge pull request #1227 from dbgate/feature/execute-current-line
Execute Current, Execute Current Line Not Current Query #1209
2025-10-21 15:27:09 +02:00
Stela Augustinova e1de4f5c5f Execute Current, Execute Current Line Not Current Query #1209 2025-10-21 14:18:22 +02:00
Jan Prochazka b6145d6f1e Merge pull request #1226 from dbgate/feature/close-tabs
message: close right side tabs #1219
2025-10-21 14:15:25 +02:00
Stela Augustinova 3804a87cef message: close right side tabs #1219 2025-10-21 12:43:44 +02:00
SPRINX0\prochazka a7a6c664c8 SYNC: #1220 fixed typo 2025-10-21 10:42:26 +02:00
SPRINX0\prochazka f075515607 v6.6.6-premium-beta.2 2025-10-21 09:30:08 +02:00
SPRINX0\prochazka 84c15bbc69 v6.6.6-beta.1 2025-10-21 09:29:58 +02:00
SPRINX0\prochazka 54e70d490d SYNC: fixed circular dependency 2025-10-21 06:10:16 +00:00
SPRINX0\prochazka 67c3de1f5d Merge branch 'feature/dynamic-promo-widget' 2025-10-20 15:28:29 +02:00
SPRINX0\prochazka 7281b5b1d7 highlight promo 2025-10-20 14:24:35 +02:00
SPRINX0\prochazka 966eb01f1c dynamic promo widget data 2025-10-20 14:00:59 +02:00
SPRINX0\prochazka 5bdf072cdf promo widget validity 2025-10-20 12:54:24 +02:00
SPRINX0\prochazka 59c3381962 promo widget WIP 2025-10-17 17:30:27 +02:00
SPRINX0\prochazka 1fa4216b18 dynamic promo widget 2025-10-17 14:04:08 +02:00
SPRINX0\prochazka a98c953876 fixed link 2025-10-17 13:05:25 +02:00
SPRINX0\prochazka bf995e5861 don't send country to cloud update - is not really needed 2025-10-17 12:51:30 +02:00
SPRINX0\prochazka 1b8470df38 use DBG info 2025-10-17 11:59:52 +02:00
SPRINX0\prochazka 98ded8ea30 added placehorder file for premium 2025-10-16 10:55:53 +02:00
CI workflows b72a50eb7e chore: auto-update github workflows 2025-10-16 08:52:21 +00:00
CI workflows 9c3f4fbb9d Update pro ref 2025-10-16 08:52:04 +00:00
Jan Prochazka 28c68308a9 SYNC: Merge pull request #15 from dbgate/feature/redis-cluster 2025-10-16 08:51:49 +00:00
CI workflows ea3b0c15ac chore: auto-update github workflows 2025-10-15 10:20:08 +00:00
CI workflows 38ce46adb0 Update pro ref 2025-10-15 10:19:48 +00:00
SPRINX0\prochazka 0a9a0103dd changelog 2025-10-15 11:05:31 +02:00
SPRINX0\prochazka b7b370ff62 v6.6.5 2025-10-15 10:50:39 +02:00
SPRINX0\prochazka 7b0c98ad2c v6.6.5-premium-beta.2 2025-10-14 15:12:07 +02:00
CI workflows bfe25e70d6 chore: auto-update github workflows 2025-10-14 13:07:25 +00:00
CI workflows b2409df369 Update pro ref 2025-10-14 13:07:03 +00:00
SPRINX0\prochazka e1d8549730 SYNC: explain query error 2025-10-14 13:06:53 +00:00
SPRINX0\prochazka 865cc081ce SYNC: database chat test moved 2025-10-14 12:48:28 +00:00
CI workflows 867d5a9eb5 chore: auto-update github workflows 2025-10-14 11:00:02 +00:00
CI workflows ac84b7604b Update pro ref 2025-10-14 10:59:42 +00:00
SPRINX0\prochazka 8f860ad93e SYNC: chart test 2025-10-14 10:59:30 +00:00
SPRINX0\prochazka f3b65700d7 fixed build of Community edition 2025-10-14 11:20:31 +02:00
CI workflows 8ec1856206 chore: auto-update github workflows 2025-10-14 09:02:33 +00:00
CI workflows 811d2162fc Update pro ref 2025-10-14 09:02:16 +00:00
Jan Prochazka 5e2cdca103 SYNC: Merge pull request #14 from dbgate/ai-sql 2025-10-14 09:02:04 +00:00
SPRINX0\prochazka 23fb5852ba v6.6.5-premium-beta.1 2025-10-10 16:27:47 +02:00
CI workflows 75cbc0d29a chore: auto-update github workflows 2025-10-10 14:08:48 +00:00
CI workflows d08cd684c5 Update pro ref 2025-10-10 14:08:31 +00:00
CI workflows 529b297ba6 chore: auto-update github workflows 2025-10-10 13:54:02 +00:00
CI workflows 32193eef49 Update pro ref 2025-10-10 13:53:47 +00:00
Jan Prochazka 1f97b90b2d Merge pull request #1205 from dbgate/feature/pin-unpin-single-database-connections
feat: allow pinning and unpinning single database connections
2025-10-10 12:57:14 +02:00
SPRINX0\prochazka 0dd36260e9 SYNC: fixed Cannot open up large JSON file #1215 2025-10-10 10:49:25 +00:00
CI workflows 3571d49987 chore: auto-update github workflows 2025-10-09 15:58:36 +00:00
CI workflows ad3489c491 Update pro ref 2025-10-09 15:58:17 +00:00
CI workflows 2461fa2e25 chore: auto-update github workflows 2025-10-09 15:55:45 +00:00
CI workflows 60e49ba343 Update pro ref 2025-10-09 15:47:43 +00:00
SPRINX0\prochazka a709381980 SYNC: try to fix TE editing problem 2025-10-09 15:33:58 +00:00
SPRINX0\prochazka c2805c8c1c use link www.dbgate.io 2025-10-08 11:19:15 +02:00
SPRINX0\prochazka 0346cbe911 SYNC: try to fix screenshot 2025-10-03 08:06:23 +00:00
CI workflows 74a4d4455b chore: auto-update github workflows 2025-10-03 07:38:01 +00:00
CI workflows 3659e1c91f Update pro ref 2025-10-03 07:37:43 +00:00
SPRINX0\prochazka 09da5c6968 SYNC: test fix 2025-10-03 07:37:32 +00:00
SPRINX0\prochazka 2575efd28d SYNC: fix 2025-10-03 06:36:41 +00:00
CI workflows 78c1c8d2b1 chore: auto-update github workflows 2025-10-03 06:24:14 +00:00
CI workflows d5147f3dbb Update pro ref 2025-10-03 06:23:56 +00:00
CI workflows 593580fbc1 chore: auto-update github workflows 2025-10-02 15:42:25 +00:00
CI workflows 67ca1cb638 Update pro ref 2025-10-02 15:42:02 +00:00
SPRINX0\prochazka bcf5b64545 SYNC: LLM provider config test 2025-10-02 15:41:51 +00:00
SPRINX0\prochazka e37ad663b3 longer timeout 2025-10-02 16:38:41 +02:00
SPRINX0\prochazka 4ce7582a46 changelog 2025-10-02 16:38:33 +02:00
SPRINX0\prochazka 1c371bb7bf v6.6.4 2025-10-02 16:23:57 +02:00
CI workflows 17fdeb0734 chore: auto-update github workflows 2025-10-02 12:32:59 +00:00
SPRINX0\prochazka 5c6f0c32b3 fix 2025-10-02 14:32:36 +02:00
SPRINX0\prochazka e630280673 v6.6.4-beta.11 2025-10-02 13:13:47 +02:00
SPRINX0\prochazka 7c87961adf v6.6.4-premium-beta.10 2025-10-02 13:13:37 +02:00
SPRINX0\prochazka 0df5ceb7d2 shared cloud folders also for community (readonly) 2025-10-02 13:11:22 +02:00
CI workflows 54342f2592 chore: auto-update github workflows 2025-10-02 10:14:37 +00:00
CI workflows fbad558c37 Update pro ref 2025-10-02 10:14:21 +00:00
CI workflows b27dfb290c chore: auto-update github workflows 2025-10-02 08:56:55 +00:00
CI workflows 063c930349 Update pro ref 2025-10-02 08:56:40 +00:00
SPRINX0\prochazka c1f1e489a7 team files controller 2025-09-29 11:18:53 +02:00
CI workflows 62960ed8de chore: auto-update github workflows 2025-09-29 09:11:45 +00:00
CI workflows 03305e04a7 Update pro ref 2025-09-29 09:11:29 +00:00
SPRINX0\prochazka 4b294b1125 v6.6.4-premium-beta.9 2025-09-26 17:06:20 +02:00
CI workflows 7122a21591 chore: auto-update github workflows 2025-09-26 15:02:10 +00:00
CI workflows 9682e571a2 Update pro ref 2025-09-26 15:01:52 +00:00
SPRINX0\prochazka 2cefbfb8aa SYNC: fixes 2025-09-26 15:01:40 +00:00
CI workflows 084062488c chore: auto-update github workflows 2025-09-26 14:52:55 +00:00
CI workflows 4dbe2b5297 Update pro ref 2025-09-26 14:52:27 +00:00
CI workflows 0391e5bc3d Update pro ref 2025-09-26 14:51:57 +00:00
SPRINX0\prochazka bcbd96c608 SYNC: chart - support group by week 2025-09-26 14:51:45 +00:00
SPRINX0\prochazka 6a6633e151 SYNC: fix 2025-09-26 13:22:26 +00:00
SPRINX0\prochazka 5c4546a54c v6.6.4-premium-beta.8 2025-09-26 14:19:42 +02:00
CI workflows 255e328340 chore: auto-update github workflows 2025-09-26 12:14:15 +00:00
CI workflows aaf9b085d7 Update pro ref 2025-09-26 12:13:58 +00:00
SPRINX0\prochazka c7b14c9fab SYNC: Merge branch 'feature/team-files' 2025-09-26 12:13:45 +00:00
CI workflows 0436ba78e2 chore: auto-update github workflows 2025-09-26 10:45:03 +00:00
CI workflows 1085a1c221 Update pro ref 2025-09-26 10:44:50 +00:00
Jan Prochazka 494b33bd7a SYNC: Merge pull request #12 from dbgate/feature/team-files 2025-09-26 10:44:39 +00:00
SPRINX0\prochazka 925e3a67da fixed for shorten names 2025-09-25 10:48:43 +02:00
SPRINX0\prochazka 78026f7fa5 Shorten identifiers 2025-09-25 10:38:14 +02:00
SPRINX0\prochazka 9d77cac4bb filter only tables with rows 2025-09-25 09:23:52 +02:00
SPRINX0\prochazka 6747280964 table row count in firebird 2025-09-25 09:11:56 +02:00
SPRINX0\prochazka d7dbd79f7c fixed loading structure for firebird 2025-09-25 08:59:12 +02:00
SPRINX0\prochazka aec692c402 simplified blob loading for firebird 2025-09-25 08:33:28 +02:00
CI workflows 113bbead4a chore: auto-update github workflows 2025-09-24 15:25:42 +00:00
CI workflows 1361c196da Update pro ref 2025-09-24 15:25:23 +00:00
SPRINX0\prochazka 987995ad68 SYNC: upgraded query splitter 2025-09-24 15:25:12 +00:00
SPRINX0\prochazka d24db7c053 using firebird splitter options 2025-09-24 17:23:09 +02:00
CI workflows 25a9d52d86 chore: auto-update github workflows 2025-09-24 15:10:12 +00:00
CI workflows 946c632920 Update pro ref 2025-09-24 15:09:54 +00:00
SPRINX0\prochazka 94bcbb80fd SYNC: upgraded query splitter 2025-09-24 15:09:42 +00:00
SPRINX0\prochazka ba58965770 firebird small refactor 2025-09-24 13:25:12 +02:00
SPRINX0\prochazka e50ddbf348 v6.6.4-premium-beta.7 2025-09-24 10:56:14 +02:00
SPRINX0\prochazka e95f21fa9c SYNC: fixed app log tab 2025-09-24 08:48:30 +00:00
SPRINX0\prochazka 7026b765bd v6.6.4-premium-beta.6 2025-09-24 10:31:17 +02:00
SPRINX0\prochazka 53eedd2701 SYNC: better DB analysis logging 2025-09-24 08:30:13 +00:00
SPRINX0\prochazka 9886c58681 SYNC: imrpoved logging of DB analyser 2025-09-24 08:12:39 +00:00
Pavel 953f6da7d7 feat: allow pinning and unpinning single database connections 2025-09-13 22:01:13 +02:00
Jan Prochazka da1efe880d v6.6.4-beta.5 2025-09-12 11:42:09 +02:00
Jan Prochazka bd0b6dd4d2 remove sourcemaps from build 2025-09-12 11:41:59 +02:00
SPRINX0\prochazka 1c049fe1fb v6.6.4-premium-beta.4 2025-09-11 16:04:25 +02:00
SPRINX0\prochazka 10b1b87d55 flexColContainer fixed 2025-09-11 16:02:28 +02:00
SPRINX0\prochazka 3ec6a3b3f2 v6.6.4-premium-beta.3 2025-09-11 15:27:58 +02:00
SPRINX0\prochazka 8da919d4cd missing file 2025-09-11 15:26:13 +02:00
SPRINX0\prochazka 5cd59b795b SYNC: fix 2025-09-11 13:20:09 +00:00
CI workflows e4dc30d1fb chore: auto-update github workflows 2025-09-11 12:53:50 +00:00
CI workflows 2013cee298 Update pro ref 2025-09-11 12:53:33 +00:00
Jan Prochazka 1f89a6304b SYNC: Merge pull request #10 from dbgate/feat-chat-compl-api 2025-09-11 12:53:21 +00:00
SPRINX0\prochazka 580e0f9df7 SYNC: fixed load apps where there is no apps dir 2025-09-11 11:47:36 +00:00
CI workflows 2221c4548e chore: auto-update github workflows 2025-09-11 11:11:37 +00:00
CI workflows e06c226e84 Update pro ref 2025-09-11 11:11:19 +00:00
Jan Prochazka 11a4f0ef32 SYNC: Merge pull request #9 from dbgate/feature/apps 2025-09-11 11:11:08 +00:00
SPRINX0\prochazka ef15f299d2 v6.6.4-beta.2 2025-09-05 13:10:27 +02:00
SPRINX0\prochazka 1d333b9322 Fixed: does no longer work with Cockroach DB #1202 2025-09-05 13:08:21 +02:00
CI workflows 5302ed8653 chore: auto-update github workflows 2025-09-04 08:15:02 +00:00
CI workflows 2cf26a10c4 Update pro ref 2025-09-04 08:14:44 +00:00
SPRINX0\prochazka deda1e4251 SYNC: fixed authorization select 2025-09-04 08:14:33 +00:00
SPRINX0\prochazka c042bf2d15 SYNC: commented out feedback menu 2025-09-04 07:20:22 +00:00
SPRINX0\prochazka 8ced6aa205 SYNC: settings permissions 2025-09-04 07:16:21 +00:00
SPRINX0\prochazka 3ca514c85b fixed error 2025-09-03 16:29:43 +02:00
CI workflows 27b8e7d5ec chore: auto-update github workflows 2025-09-03 14:22:22 +00:00
SPRINX0\prochazka a2d77a3917 removed upload error to Gist feature 2025-09-03 16:22:06 +02:00
CI workflows c0549fe422 chore: auto-update github workflows 2025-09-03 14:15:46 +00:00
CI workflows 0dc8d6fd68 Update pro ref 2025-09-03 14:15:32 +00:00
SPRINX0\prochazka cd97647818 SYNC: next permissions 2025-09-03 14:15:22 +00:00
CI workflows fcb5811f37 chore: auto-update github workflows 2025-09-03 13:30:37 +00:00
CI workflows a239ba2211 Update pro ref 2025-09-03 13:30:20 +00:00
SPRINX0\prochazka e8dc96bcda v6.6.4-beta.1 2025-09-03 10:40:05 +02:00
SPRINX0\prochazka 096ad97a73 SYNC: Fixed DbGate Web UI Connections do not display 'Databases' #1199 2025-09-03 08:37:50 +00:00
SPRINX0\prochazka a5a5517555 changelog 2025-09-01 15:46:09 +02:00
SPRINX0\prochazka d9b88a5d8d v6.6.3 2025-09-01 15:22:28 +02:00
SPRINX0\prochazka 8493ea22eb v6.6.3-premium-beta.1 2025-09-01 09:51:40 +02:00
SPRINX0\prochazka aebb87aa20 Fixed - error listing databases from Azure SQL SERVER #1197 2025-09-01 09:09:04 +02:00
SPRINX0\prochazka 14e97cb24f fixed mongoDB rename collection #1198 2025-09-01 08:57:30 +02:00
SPRINX0\prochazka 26cc15b4a2 save zoom level 2025-09-01 08:40:20 +02:00
SPRINX0\prochazka 50ce606e12 v6.6.2 2025-08-29 09:24:47 +02:00
SPRINX0\prochazka b052320f98 changelog 2025-08-29 09:23:30 +02:00
SPRINX0\prochazka ecb3cebc9f v6.6.2-beta.7 2025-08-29 08:59:02 +02:00
SPRINX0\prochazka 4a32dfc71b fixed: DBGate cannot execute any queries and the process is killed #1195 2025-08-29 08:39:19 +02:00
SPRINX0\prochazka c913929ff9 changelog 2025-08-29 08:31:14 +02:00
SPRINX0\prochazka d7d5b29b07 change log 2025-08-28 17:55:22 +02:00
SPRINX0\prochazka 111a7f72f8 v6.6.2-premium-beta.6 2025-08-28 15:07:14 +02:00
SPRINX0\prochazka 149abdef9b v6.6.2-premium-beta.3 2025-08-28 15:07:00 +02:00
Jan Prochazka 980848f35a Merge pull request #1194 from dbgate/feature/mssql-better-query-for-desc
feat: add ep.class = 1 filter
2025-08-28 15:05:22 +02:00
Pavel d1e0c86a71 feat: add ep.class = 1 filter 2025-08-26 18:05:42 +02:00
SPRINX0\prochazka d3872ca8a3 Redis "Scan all" button 2025-08-26 10:48:19 +02:00
SPRINX0\prochazka 003dec269a Allow max page size 50000 #1185 2025-08-26 10:30:30 +02:00
SPRINX0\prochazka e88092cde7 v6.6.2-premium-beta.5 2025-08-26 10:11:16 +02:00
SPRINX0\prochazka 98422bd355 changelog 2025-08-26 10:10:58 +02:00
CI workflows af83b89812 chore: auto-update github workflows 2025-08-26 07:45:32 +00:00
CI workflows cffb1b8713 Update pro ref 2025-08-26 07:45:06 +00:00
CI workflows 0b4895addf chore: auto-update github workflows 2025-08-26 07:27:54 +00:00
CI workflows 08646ea12a Update pro ref 2025-08-26 07:27:36 +00:00
CI workflows 40beb7ceeb chore: auto-update github workflows 2025-08-25 14:37:58 +00:00
CI workflows e7963aa324 Update pro ref 2025-08-25 14:37:41 +00:00
CI workflows d5894b9fb7 chore: auto-update github workflows 2025-08-25 14:25:01 +00:00
CI workflows 75c47a1113 Update pro ref 2025-08-25 14:24:46 +00:00
SPRINX0\prochazka 97923b19bf SYNC: roles UX 2025-08-25 14:24:33 +00:00
SPRINX0\prochazka 879c89a285 v6.6.2-beta.4 2025-08-25 10:06:03 +02:00
SPRINX0\prochazka 718727462b v6.6.2-premium-beta.3 2025-08-25 10:05:53 +02:00
SPRINX0\prochazka 63f2fd864a SYNC: fixed 2025-08-22 12:50:45 +00:00
SPRINX0\prochazka 556dda5790 SYNC: about window screenshot 2025-08-22 12:24:33 +00:00
SPRINX0\prochazka aeaa8549e3 SYNC: renamed screenshot 2025-08-22 12:09:06 +00:00
CI workflows 1a8a757912 chore: auto-update github workflows 2025-08-22 11:41:07 +00:00
CI workflows c69bb8acc9 Update pro ref 2025-08-22 11:40:52 +00:00
SPRINX0\prochazka 4989d67b92 v6.6.2-premium-beta.2 2025-08-22 12:32:41 +02:00
SPRINX0\prochazka b272d342b0 Merge branch 'feature/mongo-server-summary' 2025-08-22 12:32:31 +02:00
SPRINX0\prochazka 251b2853e0 v6.6.2-premium-beta.1 2025-08-22 12:00:23 +02:00
CI workflows d381c9505f chore: auto-update github workflows 2025-08-22 08:34:07 +00:00
CI workflows eeb3b8f939 Update pro ref 2025-08-22 08:33:50 +00:00
SPRINX0\prochazka ee40f32b0c SYNC: fixed permission check, new permission test 2025-08-22 08:33:39 +00:00
CI workflows 02a69ea6d9 chore: auto-update github workflows 2025-08-22 07:46:24 +00:00
CI workflows d47bb5ecd4 Update pro ref 2025-08-22 07:46:05 +00:00
Jan Prochazka d2d6e2f554 SYNC: Merge pull request #8 from dbgate/feature/db-table-permissions 2025-08-22 07:45:53 +00:00
Jan Prochazka f48b4a6c62 Merge pull request #1186 from dbgate/feature/mongo-server-summary
Feature/mongo server summary
2025-08-21 15:46:34 +02:00
Pavel 07f7b7df1b fix: send empty string instead of null when process has no operation 2025-08-21 14:45:03 +02:00
Pavel 26486f9d63 style: full width + scroll for child1 wrapper in SummaryProcesses splitter 2025-08-21 14:44:31 +02:00
Pavel 428aa970b9 Merge branch 'master' into feature/mongo-server-summary 2025-08-21 14:27:27 +02:00
SPRINX0\prochazka 2a8c532786 fixes 2025-08-21 13:00:33 +02:00
Pavel 4381829d16 fix: use full process list and info for operaiton in mysql server summary 2025-08-19 18:43:08 +02:00
Pavel 176d75768f feat: vertical split for process operation 2025-08-19 18:40:08 +02:00
Pavel e28e363bd0 feat: keep tabs and table headers sticky, scroll table bodies 2025-08-19 18:05:07 +02:00
Pavel 114ce1ea3a feat: make summary table sortable, filtrable, with sticky header 2025-08-19 17:13:19 +02:00
Pavel 78215552bf chore: add logging for server summary 2025-08-19 17:08:15 +02:00
SPRINX0\prochazka be4fe6ab77 removed obsolete method 2025-08-19 12:26:31 +02:00
Pavel ab924f6b48 feat: confirm kill process 2025-08-14 22:55:36 +02:00
Pavel e4bf2b4c9b feat: mssql server summary 2025-08-14 22:48:51 +02:00
Pavel c49b1a46f8 feat: mysql server summary 2025-08-14 22:48:42 +02:00
Pavel 6a56726734 fix: refresh proccesses only if summary tab is opened 2025-08-14 22:48:35 +02:00
Pavel 8f6783792f feat: add error handler for server summary tab 2025-08-14 21:48:56 +02:00
Pavel b5ab1d6b33 fix: always convert seconds to fixed 3 when printing running time 2025-08-14 21:36:34 +02:00
Pavel 25fe1d03a7 feat: server summary for postgres 2025-08-14 21:33:33 +02:00
Pavel 90546ad4a7 feat: toasts when killing db proc 2025-08-14 21:16:53 +02:00
Pavel 939bbc3f2c chore: update summary typing 2025-08-14 20:09:47 +02:00
Pavel 02ee327595 feat: format fileSize cols in summary databases 2025-08-14 19:21:04 +02:00
Pavel d8081277ee feat: parse col to row formatter 2025-08-14 19:18:42 +02:00
Pavel 164a112e0c feat: refresh processes only if processes tab is open 2025-08-14 19:18:33 +02:00
Pavel 697bde7b53 feat: pass tabVisible to tabs 2025-08-14 19:18:14 +02:00
Pavel 2fd5244f85 feat: use _t translations in server summary 2025-08-14 18:46:55 +02:00
SPRINX0\prochazka 354d925f94 v6.6.1 2025-08-14 17:30:52 +02:00
SPRINX0\prochazka 88c74f020c changelog 2025-08-14 17:29:59 +02:00
SPRINX0\prochazka 9b7021b1cd v6.6.1-beta.19 2025-08-14 17:06:20 +02:00
SPRINX0\prochazka 30dbb23330 fixed potential error 2025-08-14 17:05:56 +02:00
Pavel b1696ed1cd feat: autorefresh processes, refresh processes w/o displaying loader 2025-08-14 16:40:20 +02:00
Pavel 61f1c99791 feat: send cols from list databases 2025-08-14 16:39:54 +02:00
Jan Prochazka bb9a559b80 Merge pull request #1184 from daujyungx/master
fix: #340, SQL Server tedious driver connect to named instance
2025-08-14 12:17:12 +02:00
Jan Prochazka 5e20ea4975 Merge pull request #1183 from dbgate/feature/1181-oracle-lob-handler
feat: normalize oracle LOB values
2025-08-14 12:13:14 +02:00
daujyungx c5d7e30bed fix: #340, SQL Server tedious driver connect to named instance 2025-08-14 15:50:46 +08:00
Pavel de5f3a31ed fix: fetch oracle blobs as buffer 2025-08-13 14:47:59 +02:00
Jan Prochazka 7913c4135f v6.6.1-premium-beta.18 2025-08-13 07:22:17 +02:00
Jan Prochazka d49345de9c v6.6.1-beta.18 2025-08-13 07:22:06 +02:00
Jan Prochazka 1dbfa71bde v6.6.1-beta.16 2025-08-13 07:21:34 +02:00
Jan Prochazka d46b84f0d6 Merge pull request #1171 from dbgate/feature/1137-mssql-column-desc
feat: add MS_Description to mssql analyzer columns
2025-08-13 06:55:35 +02:00
Pavel 9d456992cf feat: kill db process 2025-08-13 04:54:15 +02:00
Pavel 5dd62ad2aa feat: new server summary tab 2025-08-13 04:53:25 +02:00
Pavel a293eeb398 feat: new mongo server summary 2025-08-13 04:53:24 +02:00
Pavel a6b6b5eb70 feat: add typing for listVariables, listProccess, killProccess, update server summary typings 2025-08-13 04:53:24 +02:00
Pavel 971af1df5f fix: use baseColumns for tables hashes in _runAnalysis 2025-08-12 15:44:22 +02:00
Pavel 21641da0bf feat: safeCommentChanges flag to dialect 2025-08-07 15:32:24 +02:00
Pavel a8d9c145e6 Merge branch 'master' into feature/1137-mssql-column-desc 2025-08-07 14:21:57 +02:00
Pavel bfafcb76ba fix: use only cols with comments for obj hash 2025-08-07 14:21:57 +02:00
Pavel 52f74f1204 Merge branch 'master' into feature/1137-mssql-column-desc 2025-08-07 12:53:09 +02:00
SPRINX0\prochazka 00c212ecb2 v6.6.1-premium-beta.15 2025-08-07 09:57:18 +02:00
CI workflows e77d302a49 chore: auto-update github workflows 2025-08-07 07:37:04 +00:00
CI workflows 5183f3729c Update pro ref 2025-08-07 07:36:45 +00:00
SPRINX0\prochazka e93102f105 v6.6.1-premium-beta.14 2025-08-07 07:46:26 +02:00
CI workflows a4652689ec chore: auto-update github workflows 2025-08-06 14:42:42 +00:00
CI workflows ede1005087 Update pro ref 2025-08-06 14:42:23 +00:00
SPRINX0\prochazka e97c7ed32e SYNC: fix BE 2025-08-06 14:42:10 +00:00
SPRINX0\prochazka 9230a2ab73 SYNC: Merge branch 'feature/mongosh' 2025-08-06 12:56:16 +00:00
SPRINX0\prochazka 01ee66ec4f SYNC: fixed choosing default database 2025-08-06 12:14:29 +00:00
SPRINX0\prochazka 4c12cbd3cc SYNC: improved log export 2025-08-06 11:37:49 +00:00
SPRINX0\prochazka d8eeeaaef6 SYNC: export logs 2025-08-06 11:29:24 +00:00
SPRINX0\prochazka 994dae2a7d SYNC: export logs to file 2025-08-06 11:17:05 +00:00
CI workflows 51da6e928d chore: auto-update github workflows 2025-08-06 10:35:50 +00:00
CI workflows a328ad030e Update pro ref 2025-08-06 10:35:32 +00:00
SPRINX0\prochazka ed7605eccd SYNC: Merge branch 'feature/dblogs' 2025-08-06 10:35:21 +00:00
Pavel de43880a1c feat: fetch only base column info for modifications, drop columnComment only if exists 2025-08-06 09:36:16 +02:00
Pavel 32b1a5b22d feat: include comments in contentHash for mssql 2025-08-06 09:36:16 +02:00
Pavel 795992fb42 feat: add add comment to table teest 2025-08-06 09:36:16 +02:00
Pavel 339eab33c8 feat: add add comment to column test 2025-08-06 09:36:16 +02:00
Pavel 489f3aa19d feat: add test for table creation with comments 2025-08-06 09:36:16 +02:00
Pavel 888e284f84 fix:fix: removeu duplicate method, simplify changeColumnComment 2025-08-06 09:36:16 +02:00
Pavel d151114f08 fix: correctly parse table comment when creating a table 2025-08-06 09:36:16 +02:00
Pavel 4f6a3c23ad feat: ms_description for tables, upd columns 2025-08-06 09:36:16 +02:00
CI workflows 4ed437fd4e chore: auto-update github workflows 2025-08-05 15:15:45 +00:00
CI workflows fa0b21ba81 Update pro ref 2025-08-05 15:14:18 +00:00
Jan Prochazka a96f1d0b49 SYNC: Merge pull request #7 from dbgate/feature/applog 2025-08-05 15:14:06 +00:00
SPRINX0\prochazka ac0aebd751 SYNC: strict delimit logs by date 2025-08-04 10:52:45 +00:00
SPRINX0\prochazka 781cbb4668 SYNC: updated mongo version 2025-08-04 08:34:43 +00:00
SPRINX0\prochazka 56ca1911a1 SYNC: mongosh - use only in stream method 2025-08-01 14:21:45 +00:00
Pavel c20aec23a2 feat: change ms_description when columnComment changes 2025-07-31 15:01:22 +02:00
Pavel a9cff01579 fix: show columnComment in structure ui 2025-07-31 15:01:22 +02:00
Pavel 6af56a61b8 feat: add ignoreComments options testEqualColumns 2025-07-31 15:01:22 +02:00
Pavel 252db191a6 feat: add ms_description to mssqldumper table options 2025-07-31 15:01:22 +02:00
Pavel 5e2776f264 feat: add MS_Description to mssql analyzer columns 2025-07-31 15:01:22 +02:00
CI workflows 7ec9fb2c44 chore: auto-update github workflows 2025-07-31 10:52:36 +00:00
SPRINX0\prochazka 34facb6b3b try to fix build 2025-07-31 12:52:17 +02:00
SPRINX0\prochazka 5bb2a1368e v6.6.1-premium-beta.13 2025-07-31 10:49:08 +02:00
CI workflows 85a7bbca66 chore: auto-update github workflows 2025-07-31 08:47:07 +00:00
CI workflows 2b7f27bf8f Update pro ref 2025-07-31 08:46:50 +00:00
SPRINX0\prochazka dd57945e7d v6.6.1-premium-beta.12 2025-07-31 10:26:58 +02:00
SPRINX0\prochazka fa02b4fd56 SYNC: tag fix 2025-07-31 08:26:35 +00:00
SPRINX0\prochazka c8715eead5 v6.6.1-premium-beta.11 2025-07-31 09:56:51 +02:00
SPRINX0\prochazka 37aae8c10e SYNC: GTM support 2025-07-31 07:55:58 +00:00
SPRINX0\prochazka a97ed02e15 support for script embeding 2025-07-31 09:34:45 +02:00
383 changed files with 13471 additions and 3787 deletions
+41 -11
View File
@@ -6,9 +6,13 @@ name: Electron app BETA
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy:
fail-fast: false
matrix:
@@ -53,12 +57,6 @@ jobs:
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillPackagedPlugins
run: |
@@ -66,21 +64,53 @@ jobs:
- name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: |
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_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 }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts
run: |
mkdir artifacts
+41 -11
View File
@@ -6,9 +6,13 @@ name: Electron app check build
push:
tags:
- check-[0-9]+-[0-9]+-[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy:
fail-fast: false
matrix:
@@ -49,12 +53,6 @@ jobs:
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillPackagedPlugins
run: |
@@ -62,21 +60,53 @@ jobs:
- name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: |
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_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 }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts
run: |
mkdir artifacts
+48 -14
View File
@@ -6,9 +6,13 @@ name: Electron app PREMIUM BETA
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy:
fail-fast: false
matrix:
@@ -39,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -81,37 +85,67 @@ jobs:
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillPackagedPlugins
run: |
cd ..
cd dbgate-merged
yarn fillPackagedPlugins
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_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 }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: ../dbgate-merged/app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run fixYmlHashes
- name: Copy artifacts
run: |
mkdir artifacts
+48 -14
View File
@@ -6,9 +6,13 @@ name: Electron app PREMIUM
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy:
fail-fast: false
matrix:
@@ -39,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -81,37 +85,67 @@ jobs:
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillPackagedPlugins
run: |
cd ..
cd dbgate-merged
yarn fillPackagedPlugins
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_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 }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: ../dbgate-merged/app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run fixYmlHashes
- name: Copy artifacts
run: |
mkdir artifacts
+41 -11
View File
@@ -6,9 +6,13 @@ name: Electron app
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy:
fail-fast: false
matrix:
@@ -49,12 +53,6 @@ jobs:
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: fillPackagedPlugins
run: |
@@ -62,24 +60,56 @@ jobs:
- name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: |
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_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 }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: generatePadFile
run: |
yarn generatePadFile
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts
run: |
mkdir artifacts
+1 -8
View File
@@ -39,7 +39,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -66,13 +66,6 @@ jobs:
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: Prepare packer build
run: |
cd ..
+1 -9
View File
@@ -44,7 +44,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -76,14 +76,6 @@ jobs:
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: Prepare docker image
run: |
cd ..
-6
View File
@@ -65,12 +65,6 @@ jobs:
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
- name: Prepare docker image
run: |
+17 -19
View File
@@ -7,6 +7,9 @@ name: NPM packages PREMIUM
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
@@ -32,7 +35,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -49,13 +52,8 @@ jobs:
cd ..
cd dbgate-merged
node adjustNpmPackageJsonPremium
- name: Configure NPM token
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cd ..
cd dbgate-merged
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: Update npm
run: npm install -g npm@latest
- name: Remove dbmodel - should be not published
run: |
cd ..
@@ -71,35 +69,35 @@ jobs:
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
- name: Compute npm dist-tag
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish dbgate-api-premium
run: |
cd ..
cd dbgate-merged/packages/api
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-web-premium
run: |
cd ..
cd dbgate-merged/packages/web
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve-premium
run: |
cd ..
cd dbgate-merged/packages/serve
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cosmosdb
run: |
cd ..
cd dbgate-merged/plugins/dbgate-plugin-cosmosdb
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-firestore
run: |
cd ..
cd dbgate-merged/plugins/dbgate-plugin-firestore
npm publish
npm publish --tag "$NPM_TAG"
+33 -31
View File
@@ -7,6 +7,9 @@ name: NPM packages
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+
permissions:
id-token: write
contents: write
jobs:
build:
runs-on: ${{ matrix.os }}
@@ -26,108 +29,107 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: 22.x
- name: Configure NPM token
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: Update npm
run: npm install -g npm@latest
- name: yarn install
run: |
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
- name: Compute npm dist-tag
run: |
yarn printSecrets
env:
GIST_UPLOAD_SECRET: ${{secrets.GIST_UPLOAD_SECRET}}
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish types
working-directory: packages/types
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish tools
working-directory: packages/tools
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish sqltree
working-directory: packages/sqltree
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish api
working-directory: packages/api
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish datalib
working-directory: packages/datalib
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish filterparser
working-directory: packages/filterparser
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish web
working-directory: packages/web
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve
working-directory: packages/serve
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbmodel
working-directory: packages/dbmodel
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-xml
working-directory: plugins/dbgate-plugin-xml
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-excel
working-directory: plugins/dbgate-plugin-excel
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mssql
working-directory: plugins/dbgate-plugin-mssql
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mysql
working-directory: plugins/dbgate-plugin-mysql
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mongo
working-directory: plugins/dbgate-plugin-mongo
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-postgres
working-directory: plugins/dbgate-plugin-postgres
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-sqlite
working-directory: plugins/dbgate-plugin-sqlite
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-redis
working-directory: plugins/dbgate-plugin-redis
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-oracle
working-directory: plugins/dbgate-plugin-oracle
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-clickhouse
working-directory: plugins/dbgate-plugin-clickhouse
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-dbf
working-directory: plugins/dbgate-plugin-dbf
run: |
npm publish
npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cassandra
working-directory: plugins/dbgate-plugin-cassandra
run: |
npm publish
npm publish --tag "$NPM_TAG"
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 36b6ce878c3c0a0c9623163c8a8b3bdeefc7da53
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -107,7 +107,7 @@ jobs:
ports:
- '16009:5556'
mongo:
image: mongo:6.0.25
image: mongo:4.4.29
env:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: Pwd2020Db
+1 -1
View File
@@ -83,7 +83,7 @@ jobs:
ports:
- '15002:1433'
clickhouse-integr:
image: bitnami/clickhouse:24.8.4
image: bitnamilegacy/clickhouse:24.8.4
env:
CLICKHOUSE_ADMIN_PASSWORD: Pwd2020Db
ports:
+68
View File
@@ -8,6 +8,74 @@ Builds:
- linux - application for linux
- win - application for Windows
## 6.6.11
- FIXED: Fixed theming on application startup
- CHANGED: Improved licensing page
## 6.6.10
- FIXED: License from environment variable is not refreshed #1245
- FIXED: connection closing / reconnecting #1237
- ADDED: retain history across multiple queries #1236
- ADDED: load CSVs to temp tables #1235
- FIXED: Not possible to scroll the data view horizontally by pressing shift and scroll mouse middle button on Mac #453
- FIXED: Expired trial workflow (Premium)
- ADDED: Column name collision resolving #1234 (MySQL)
## 6.6.8
- CHANGED: Windows executable now uses Azure trusted signing certificate
- CHANGED: NPM packages now use GitHub OIDC provenance signing for better security
- CHANGED: Some features moved to Premium edition (master/detail views, FK lookups, column expansion, split view, advanced export/import, data archives, grouping, macros)
## 6.6.6
- ADDED: Allow disable/re-enable filter #1174
- ADDED: Close right side tabs #1219
- ADDED: Ability disable execute current line in query editor #1209
- ADDED: Support for Redis Cluster #1204 (Premium)
## 6.6.5
- ADDED: SQL AI assistant - powered by database chat, could help you to write SQL queries (Premium)
- ADDED: Explain SQL error (powered by AI) (Premium)
- ADDED: Database chat (and SQL AI Assistant) now supports showing charts (Premium)
- FIXED: Fxied editing new files and roles (Team Premium)
- FIXED: Connection to standalone database could be now pinned
- FIXED: Cannot open up large JSON file #1215
## 6.6.4
- ADDED: AI Database chat now supports much more LLM models. (Premium)
- ADDED: Possibility to use your own API key with OPENAI-compatible providers (OpenRouter, Antropic...)
- ADDED: Possibility to use self-hosted own LLM (eg. Llama)
- ADDED: Team files - save SQL files and define shared charts, assign roles and users to these objects (Team Premium)
- FIXED: BUG: does no longer work with Cockroach DB #1202
- FIXED: DbGate Web UI Connections do not display 'Databases' #1199
- CHANGED: Redesign fof applications. Applications are now storted in single JSON file
- ADDED: Application editor (Premium)
- ADDED: Posibility to filter only tables with rows
- FIXED: Fixed several issues with large Firebird databases
- CHANGED: Community edition now supports shared folders in read-only mode
## 6.6.3
- FIXED: Error “db.getCollection(…).renameCollection is not a function” when renaming collection in dbGate #1198
- FIXED: Can't list databases from Azure SQL SERVER #1197
- ADDED: Save zoom level in electron apps
## 6.6.2
- ADDED: List of processes, ability to kill process (Server summary) #1178
- ADDED: Database and table permissions (Team Premium edition)
- ADDED: Redis search box - Scan all #1191
- FIXED: Optimalized loading SQL server with descriptions #1187
- CHANGED: Allow a much greater page size #1185
- FIXED: Optimalized loading SQL server with descriptions #1187
- FIXED: Executing queries for SQLite crash #1195
## 6.6.1
- ADDED: Support for Mongo shell (Premium) - #1114
- FIXED: Support for BLOB in Oracle #1181
- ADDED: Connect to named SQL Server instance #340
- ADDED: Support for SQL Server descriptions #1137
- ADDED: Application log viewer
- FIXED: Selecting default database in connection dialog
- CHANGED: Improved logging system, added related database and connection to logs metadata
## 6.6.0
- ADDED: Database chat - AI powered chatbot, which knows your database (Premium)
- ADDED: Firestore support (Premium)
+3 -7
View File
@@ -15,13 +15,10 @@ But there are also many advanced features like schema compare, visual query desi
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.io](https://dbgate.io/download/)
* Looking for DbGate Community? **Download** from [dbgate.org](https://dbgate.org/download/)
* **Download** application for Windows, Linux or Mac from [dbgate.io](https://www.dbgate.io/download/)
* Looking for DbGate Community? **Download** from [dbgate.io](https://www.dbgate.io/download-community/)
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
* Use nodeJs [scripting interface](https://docs.dbgate.io/scripting) ([API documentation](https://docs.dbgate.io/apidoc))
* [Recommend DbGate](https://testimonial.to/dbgate) | [Rate on G2](https://www.g2.com/products/dbgate/reviews)
* [Give us feedback](https://dbgate.org/feedback) - it will help us to decide, how to improve DbGate in future
* We [offer 2-year PREMIUM license](https://dbgate.org/review/) for any honest review on these platforms (time-limited offer)
## Supported databases
* MySQL
@@ -92,8 +89,7 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
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
* Purchase a [DbGate Premium](https://dbgate.io/purchase/premium/) liocense
* Write review on [Product Hunt](https://www.producthunt.com/products/dbgate) or [G2](https://www.g2.com/products/dbgate/reviews) - we offer [2-year PREMIUM license](https://dbgate.org/review/) for reviewers (time limited offer)
* Purchase a [DbGate Premium](https://www.dbgate.io/purchase/premium/) license
* 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)
+3 -12
View File
@@ -111,22 +111,13 @@
"src",
"icon.png",
"!node_modules/cpu-features/build/**"
],
"electronFuses": {
"runAsNode": true,
"enableCookieEncryption": true,
"enableNodeOptionsEnvironmentVariable": true,
"enableNodeCliInspectArguments": true,
"enableEmbeddedAsarIntegrityValidation": true,
"onlyLoadAppFromAsar": true,
"grantFileProtocolExtraPrivileges": false
}
]
},
"homepage": "./",
"scripts": {
"start": "cross-env ELECTRON_START_URL=http://localhost:5001 DEVMODE=1 electron .",
"start:local": "cross-env electron .",
"dist": "electron-builder",
"dist": "electron-builder --publish never",
"build": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn dist",
"build:local": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn predist",
"postinstall": "yarn rebuild && patch-package",
@@ -137,7 +128,7 @@
"devDependencies": {
"copyfiles": "^2.2.0",
"cross-env": "^6.0.3",
"electron": "30.0.2",
"electron": "38.6.0",
"electron-builder": "25.1.8"
}
}
+16 -8
View File
@@ -85,7 +85,7 @@ function formatKeyText(keyText) {
return keyText.replace('CtrlOrCommand+', 'Ctrl+');
}
function commandItem(item) {
function commandItem(item, disableAll = false) {
const id = item.command;
const command = commands[id];
if (item.skipInApp) {
@@ -95,7 +95,7 @@ function commandItem(item) {
id,
label: command ? command.menuName || command.toolbarName || command.name : id,
accelerator: formatKeyText(command ? command.keyText : undefined),
enabled: command ? command.enabled : false,
enabled: command ? command.enabled && !disableAll : false,
click() {
if (mainWindow) {
mainWindow.webContents.send('run-command', id);
@@ -107,14 +107,14 @@ function commandItem(item) {
};
}
function buildMenu() {
function buildMenu(disableAll = false) {
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true, isMac: isMac() }), item => {
if (item.divider) {
return { type: 'separator' };
}
if (item.command) {
return commandItem(item);
return commandItem(item, disableAll);
}
});
@@ -129,7 +129,7 @@ function buildMenu() {
{
label: 'DbGate',
submenu: [
commandItem({ command: 'about.show' }),
commandItem({ command: 'about.show' }, disableAll),
{ role: 'services' },
{ role: 'hide' },
{ role: 'hideOthers' },
@@ -145,7 +145,10 @@ function buildMenu() {
}
ipcMain.on('update-commands', async (event, arg) => {
commands = JSON.parse(arg);
const parsed = JSON.parse(arg);
commands = parsed.commands;
const isModalOpened = parsed.isModalOpened;
const dbgatePage = parsed.dbgatePage;
for (const key of Object.keys(commands)) {
const menu = mainMenu.getMenuItemById(key);
if (!menu) continue;
@@ -153,14 +156,14 @@ ipcMain.on('update-commands', async (event, arg) => {
// rebuild menu
if (menu.label != command.text || menu.accelerator != command.keyText) {
mainMenu = buildMenu();
mainMenu = buildMenu(isModalOpened || !!dbgatePage);
Menu.setApplicationMenu(mainMenu);
// mainWindow.setMenu(mainMenu);
return;
}
menu.enabled = command.enabled;
menu.enabled = command.enabled && !isModalOpened && !dbgatePage;
}
});
ipcMain.on('quit-app', async (event, arg) => {
@@ -212,6 +215,10 @@ ipcMain.on('app-started', async (event, arg) => {
autoUpdater.checkForUpdates();
}
}
if (initialConfig['winZoomLevel'] != null) {
mainWindow.webContents.zoomLevel = initialConfig['winZoomLevel'];
}
});
ipcMain.on('window-action', async (event, arg) => {
if (!mainWindow) {
@@ -394,6 +401,7 @@ function createWindow() {
JSON.stringify({
winBounds: mainWindow.getBounds(),
winIsMaximized: mainWindow.isMaximized(),
winZoomLevel: mainWindow.webContents.zoomLevel,
}),
'utf-8'
);
+2 -2
View File
@@ -10,6 +10,7 @@ module.exports = ({ editMenu, isMac }) => [
{ command: 'new.queryDesign', hideDisabled: true },
{ command: 'new.diagram', hideDisabled: true },
{ command: 'new.perspective', hideDisabled: true },
{ command: 'new.application', hideDisabled: true },
{ command: 'new.shell', hideDisabled: true },
{ command: 'new.jsonl', hideDisabled: true },
{ command: 'new.modelTransform', hideDisabled: true },
@@ -86,7 +87,6 @@ module.exports = ({ editMenu, isMac }) => [
{ divider: true },
{ command: 'folder.showLogs', hideDisabled: true },
{ command: 'folder.showData', hideDisabled: true },
{ command: 'new.gist', hideDisabled: true },
{ command: 'app.resetSettings', hideDisabled: true },
{ divider: true },
{ command: 'app.exportConnections', hideDisabled: true },
@@ -108,7 +108,7 @@ module.exports = ({ editMenu, isMac }) => [
{ command: 'app.openWeb', hideDisabled: true },
{ command: 'app.openIssue', hideDisabled: true },
{ command: 'app.openSponsoring', hideDisabled: true },
{ command: 'app.giveFeedback', hideDisabled: true },
// { command: 'app.giveFeedback', hideDisabled: true },
{ divider: true },
{ command: 'settings.commands', hideDisabled: true },
{ command: 'tabs.changelog', hideDisabled: true },
+279 -224
View File
@@ -16,9 +16,9 @@
ajv-keywords "^3.4.1"
"@electron/asar@^3.2.7":
version "3.2.17"
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.17.tgz#91d28087aad80d1a1c8cc4e667c6476edf50f949"
integrity sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==
version "3.4.1"
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.4.1.tgz#4e9196a4b54fba18c56cd8d5cac67c5bdc588065"
integrity sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==
dependencies:
commander "^5.0.0"
glob "^7.1.6"
@@ -98,6 +98,18 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@isaacs/balanced-match@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29"
integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==
"@isaacs/brace-expansion@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3"
integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==
dependencies:
"@isaacs/balanced-match" "^4.0.1"
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@@ -202,16 +214,23 @@
"@types/node" "*"
"@types/ms@*":
version "0.7.34"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78"
integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
"@types/node@*", "@types/node@^20.9.0":
version "20.12.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.10.tgz#8f0c3f12b0f075eee1fe20c1afb417e9765bef76"
integrity sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==
"@types/node@*":
version "24.10.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.0.tgz#6b79086b0dfc54e775a34ba8114dcc4e0221f31f"
integrity sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==
dependencies:
undici-types "~5.26.4"
undici-types "~7.16.0"
"@types/node@^22.7.7":
version "22.19.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.0.tgz#849606ef3920850583a4e7ee0930987c35ad80be"
integrity sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==
dependencies:
undici-types "~6.21.0"
"@types/plist@^3.0.1":
version "3.0.5"
@@ -229,9 +248,9 @@
"@types/node" "*"
"@types/verror@^1.10.3":
version "1.10.10"
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
integrity sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==
version "1.10.11"
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.11.tgz#d3d6b418978c8aa202d41e5bb3483227b6ecc1bb"
integrity sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==
"@types/yauzl@^2.9.1":
version "2.10.3"
@@ -241,9 +260,9 @@
"@types/node" "*"
"@xmldom/xmldom@^0.8.8":
version "0.8.10"
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==
version "0.8.11"
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608"
integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==
"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
@@ -263,14 +282,14 @@ agent-base@6, agent-base@^6.0.2:
debug "4"
agent-base@^7.1.0, agent-base@^7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
version "7.1.4"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8"
integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
agentkeepalive@^4.2.1:
version "4.5.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
version "4.6.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==
dependencies:
humanize-ms "^1.2.1"
@@ -303,9 +322,9 @@ ansi-regex@^5.0.1:
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-regex@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
version "6.2.2"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1"
integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
@@ -315,9 +334,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
color-convert "^2.0.1"
ansi-styles@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
version "6.2.3"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041"
integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==
app-builder-bin@5.0.0-alpha.10:
version "5.0.0-alpha.10"
@@ -363,9 +382,9 @@ app-builder-lib@25.1.8:
temp-file "^3.4.0"
"aproba@^1.0.3 || ^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
version "2.1.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1"
integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==
are-we-there-yet@^3.0.0:
version "3.0.1"
@@ -395,10 +414,10 @@ async-exit-hook@^2.0.1:
resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3"
integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==
async@^3.2.3:
version "3.2.5"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
async@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
asynckit@^0.4.0:
version "0.4.0"
@@ -447,33 +466,33 @@ boolean@^3.0.1:
integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
version "1.1.12"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
version "2.0.2"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.0.1"
fill-range "^7.1.1"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
buffer-equal-constant-time@1.0.1:
buffer-equal-constant-time@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
@@ -499,10 +518,10 @@ builder-util-runtime@9.2.10:
debug "^4.3.4"
sax "^1.2.4"
builder-util-runtime@9.2.5:
version "9.2.5"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.2.5.tgz#0afdffa0adb5c84c14926c7dd2cf3c6e96e9be83"
integrity sha512-HjIDfhvqx/8B3TDN4GbABQcgpewTU4LMRTQPkVpKYV3lsuxEJoIfvg09GyWTNmfVNSUAYf+fbTN//JX4TH20pg==
builder-util-runtime@9.3.1:
version "9.3.1"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz#0daedde0f6d381f2a00a50a407b166fe7dca1a67"
integrity sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==
dependencies:
debug "^4.3.4"
sax "^1.2.4"
@@ -571,7 +590,15 @@ cacheable-request@^7.0.2:
normalize-url "^6.0.1"
responselike "^2.0.0"
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -744,9 +771,9 @@ cross-env@^6.0.3:
cross-spawn "^7.0.0"
cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
version "6.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
@@ -754,26 +781,19 @@ cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3, cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^4.3.3:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
@@ -825,9 +845,9 @@ delegates@^1.0.0:
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-libc@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
version "2.1.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
detect-node@^2.0.4:
version "2.1.0"
@@ -878,9 +898,18 @@ dotenv-expand@^11.0.6:
dotenv "^16.4.5"
dotenv@^16.4.5:
version "16.4.7"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
version "16.6.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020"
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
eastasianwidth@^0.2.0:
version "0.2.0"
@@ -936,11 +965,11 @@ electron-publish@25.1.7:
mime "^2.5.2"
electron-updater@^6.3.4:
version "6.3.4"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.3.4.tgz#3934bc89875bb524c2cbbd11041114e97c0c2496"
integrity sha512-uZUo7p1Y53G4tl6Cgw07X1yF8Jlz6zhaL7CQJDZ1fVVkOaBfE2cWtx80avwDVi8jHp+I/FWawrMgTAeCCNIfAg==
version "6.6.2"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.6.2.tgz#3e65e044f1a99b00d61e200e24de8e709c69ce99"
integrity sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==
dependencies:
builder-util-runtime "9.2.5"
builder-util-runtime "9.3.1"
fs-extra "^10.1.0"
js-yaml "^4.1.0"
lazy-val "^1.0.5"
@@ -949,13 +978,13 @@ electron-updater@^6.3.4:
semver "^7.6.3"
tiny-typed-emitter "^2.1.0"
electron@30.0.2:
version "30.0.2"
resolved "https://registry.yarnpkg.com/electron/-/electron-30.0.2.tgz#95ba019216bf8be9f3097580123e33ea37497733"
integrity sha512-zv7T+GG89J/hyWVkQsLH4Y/rVEfqJG5M/wOBIGNaDdqd8UV9/YZPdS7CuFeaIj0H9LhCt95xkIQNpYB/3svOkQ==
electron@38.6.0:
version "38.6.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-38.6.0.tgz#c862bff41d42776e307bf5cc92503dda23612339"
integrity sha512-68OFNxJlrEStA+t8k5atzf4frJddvRR1N1oalr49Ll8YZ0+0nEsDhw4UNhTCoZKTjSYcxFF/4rt+sco+OlnB3g==
dependencies:
"@electron/get" "^2.0.0"
"@types/node" "^20.9.0"
"@types/node" "^22.7.7"
extract-zip "^2.0.1"
emoji-regex@^8.0.0:
@@ -976,9 +1005,9 @@ encoding@^0.1.13:
iconv-lite "^0.6.2"
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
version "1.4.5"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==
dependencies:
once "^1.4.0"
@@ -992,27 +1021,42 @@ err-code@^2.0.2:
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
dependencies:
get-intrinsic "^1.2.4"
es-define-property@^1.0.0, es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
es-set-tostringtag@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
dependencies:
es-errors "^1.3.0"
get-intrinsic "^1.2.6"
has-tostringtag "^1.0.2"
hasown "^2.0.2"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escalade@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
version "3.2.0"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
escape-string-regexp@^4.0.0:
version "4.0.0"
@@ -1020,9 +1064,9 @@ escape-string-regexp@^4.0.0:
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
exponential-backoff@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
version "3.1.3"
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz#51cf92c1c0493c766053f9d3abee4434c244d2f6"
integrity sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==
extract-zip@^2.0.1:
version "2.0.1"
@@ -1064,10 +1108,10 @@ filelist@^1.0.4:
dependencies:
minimatch "^5.0.1"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@@ -1079,20 +1123,22 @@ find-yarn-workspace-root@^2.0.0:
micromatch "^4.0.2"
foreground-child@^3.1.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==
version "3.3.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
dependencies:
cross-spawn "^7.0.0"
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
version "4.0.4"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
hasown "^2.0.2"
mime-types "^2.1.12"
fs-extra@^10.0.0, fs-extra@^10.1.0:
@@ -1105,9 +1151,9 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
universalify "^2.0.0"
fs-extra@^11.1.1:
version "11.2.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
version "11.3.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4"
integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
@@ -1168,16 +1214,29 @@ get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
get-intrinsic@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.1.1"
function-bind "^1.1.2"
has-proto "^1.0.1"
has-symbols "^1.0.3"
hasown "^2.0.0"
get-proto "^1.0.1"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
get-stream@^5.1.0:
version "5.2.0"
@@ -1241,12 +1300,10 @@ globalthis@^1.0.1:
define-properties "^1.2.1"
gopd "^1.0.1"
gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
dependencies:
get-intrinsic "^1.1.3"
gopd@^1.0.1, gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
got@^11.7.0, got@^11.8.5:
version "11.8.6"
@@ -1282,22 +1339,24 @@ has-property-descriptors@^1.0.0:
dependencies:
es-define-property "^1.0.0"
has-proto@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
has-symbols@^1.0.3, has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-tostringtag@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
dependencies:
has-symbols "^1.0.3"
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
hasown@^2.0.0:
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
@@ -1312,9 +1371,9 @@ hosted-git-info@^4.1.0:
lru-cache "^6.0.0"
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
version "4.2.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5"
integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==
http-proxy-agent@^5.0.0:
version "5.0.0"
@@ -1412,13 +1471,10 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1,
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip-address@^9.0.5:
version "9.0.5"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
dependencies:
jsbn "1.1.0"
sprintf-js "^1.1.3"
ip-address@^10.0.1:
version "10.1.0"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4"
integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==
is-ci@^2.0.0:
version "2.0.0"
@@ -1487,9 +1543,9 @@ isbinaryfile@^4.0.8:
integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==
isbinaryfile@^5.0.0:
version "5.0.4"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.4.tgz#2a2edefa76cafa66613fe4c1ea52f7f031017bdf"
integrity sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==
version "5.0.6"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.6.tgz#01eac28867aeffaebaee7eaf21d1dd3a67d7c0c7"
integrity sha512-I+NmIfBHUl+r2wcDd6JwE9yWje/PIVY/R5/CmV8dXLZd5K+L9X2klAOwfAHNnondLXkbHyTAleQAWonpTJBTtw==
isexe@^2.0.0:
version "2.0.0"
@@ -1506,14 +1562,13 @@ jackspeak@^3.1.2:
"@pkgjs/parseargs" "^0.11.0"
jake@^10.8.5:
version "10.9.1"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.1.tgz#8dc96b7fcc41cb19aa502af506da4e1d56f5e62b"
integrity sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==
version "10.9.4"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.4.tgz#d626da108c63d5cfb00ab5c25fadc7e0084af8e6"
integrity sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==
dependencies:
async "^3.2.3"
chalk "^4.0.2"
async "^3.2.6"
filelist "^1.0.4"
minimatch "^3.1.2"
picocolors "^1.1.1"
js-yaml@^4.1.0:
version "4.1.0"
@@ -1522,11 +1577,6 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
jsbn@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
@@ -1555,9 +1605,9 @@ jsonfile@^4.0.0:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
version "6.2.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62"
integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
dependencies:
universalify "^2.0.0"
optionalDependencies:
@@ -1580,11 +1630,11 @@ jsonwebtoken@^9.0.2:
semver "^7.5.4"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
version "1.4.2"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9"
integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==
dependencies:
buffer-equal-constant-time "1.0.1"
buffer-equal-constant-time "^1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
@@ -1729,12 +1779,17 @@ matcher@^3.0.0:
dependencies:
escape-string-regexp "^4.0.0"
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
micromatch@^4.0.2:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
braces "^3.0.2"
braces "^3.0.3"
picomatch "^2.3.1"
mime-db@1.52.0:
@@ -1770,13 +1825,13 @@ mimic-response@^3.1.0:
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
version "10.1.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55"
integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==
dependencies:
brace-expansion "^2.0.1"
"@isaacs/brace-expansion" "^5.0.0"
minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -1871,11 +1926,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -1892,9 +1942,9 @@ nice-try@^1.0.4:
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-abi@^3.45.0:
version "3.71.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038"
integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==
version "3.80.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.80.0.tgz#d7390951f27caa129cceeec01e1c20fc9f07581c"
integrity sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==
dependencies:
semver "^7.3.5"
@@ -1904,9 +1954,9 @@ node-addon-api@^1.6.3:
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
node-api-version@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.0.tgz#5177441da2b1046a4d4547ab9e0972eed7b1ac1d"
integrity sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==
version "0.2.1"
resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.1.tgz#19bad54f6d65628cbee4e607a325e4488ace2de9"
integrity sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==
dependencies:
semver "^7.3.5"
@@ -2081,6 +2131,11 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -2119,9 +2174,9 @@ promise-retry@^2.0.1:
retry "^0.12.0"
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
version "3.0.3"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d"
integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
@@ -2261,9 +2316,9 @@ sanitize-filename@^1.6.3:
truncate-utf8-bytes "^1.0.0"
sax@^1.2.4:
version "1.3.0"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
version "1.4.3"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.3.tgz#fcebae3b756cdc8428321805f4b70f16ec0ab5db"
integrity sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==
semver-compare@^1.0.0:
version "1.0.0"
@@ -2280,15 +2335,10 @@ semver@^6.2.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.2:
version "7.6.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2"
integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==
semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
version "7.6.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
version "7.7.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
serialize-error@^7.0.1:
version "7.0.1"
@@ -2372,11 +2422,11 @@ socks-proxy-agent@^7.0.0:
socks "^2.6.2"
socks@^2.6.2:
version "2.8.3"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5"
integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
version "2.8.7"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea"
integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==
dependencies:
ip-address "^9.0.5"
ip-address "^10.0.1"
smart-buffer "^4.2.0"
source-map-support@^0.5.19:
@@ -2392,7 +2442,7 @@ source-map@^0.6.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sprintf-js@^1.1.2, sprintf-js@^1.1.3:
sprintf-js@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
@@ -2454,9 +2504,9 @@ string_decoder@~1.1.1:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
version "7.1.2"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba"
integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==
dependencies:
ansi-regex "^6.0.1"
@@ -2522,9 +2572,9 @@ tmp@^0.0.33:
os-tmpdir "~1.0.2"
tmp@^0.2.0:
version "0.2.3"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
version "0.2.5"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8"
integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==
to-regex-range@^5.0.1:
version "5.0.1"
@@ -2546,14 +2596,19 @@ type-fest@^0.13.1:
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
typescript@^5.4.3:
version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
version "5.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.21.0:
version "6.21.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
undici-types@~7.16.0:
version "7.16.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46"
integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==
unique-filename@^2.0.0:
version "2.0.1"
@@ -2592,9 +2647,9 @@ uri-js@^4.2.2:
punycode "^2.1.0"
utf8-byte-length@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==
version "1.0.5"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e"
integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env node
// assign-dbgm-codes.mjs
import fs from 'fs/promises';
import path from 'path';
const PLACEHOLDER = 'DBGM-00000';
const CODE_RE = /DBGM-(\d{5})/g;
const JS_TS_RE = /\.(mjs|cjs|js|ts|jsx|tsx)$/i;
const IGNORE_DIRS = new Set([
'node_modules',
'.git',
'.hg',
'.svn',
'dist',
'build',
'out',
'.next',
'.turbo',
'.cache',
]);
const IGNORE_FILES = ['assign-dbgm-codes.mjs', 'package.json', 'README.md'];
// --- CLI ---
const args = process.argv.slice(2);
const dryRun = args.includes('--dry');
const rootArg = args.find(a => a !== '--dry') || process.cwd();
const root = path.resolve(rootArg);
// --- helpers ---
async function* walk(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const e of entries) {
if (e.isDirectory()) {
if (IGNORE_DIRS.has(e.name)) continue;
yield* walk(path.join(dir, e.name));
} else if (e.isFile()) {
if (JS_TS_RE.test(e.name) && !IGNORE_FILES.includes(e.name)) yield path.join(dir, e.name);
}
}
}
function formatCode(n) {
return `DBGM-${String(n).padStart(5, '0')}`;
}
// Find the smallest positive integer not in `taken`
function makeNextCodeFn(taken) {
let n = 1;
// advance n to first free
while (taken.has(n)) n++;
return () => {
const code = n;
taken.add(code);
// move n to next free for next call
do {
n++;
} while (taken.has(n));
return formatCode(code);
};
}
// --- main ---
(async () => {
console.log(`Scanning: ${root} ${dryRun ? '(dry run)' : ''}`);
// 1) Collect all taken codes across the repo
const taken = new Set(); // numeric parts only
const files = [];
for await (const file of walk(root)) files.push(file);
await Promise.all(
files.map(async file => {
try {
const text = await fs.readFile(file, 'utf8');
for (const m of text.matchAll(CODE_RE)) {
const num = Number(m[1]);
if (Number.isInteger(num) && num > 0) taken.add(num);
}
} catch (err) {
console.warn(`! Failed to read ${file}: ${err.message}`);
}
})
);
console.log(`Found ${taken.size} occupied code(s).`);
// 2) Replace placeholders with next available unique code
const nextCode = makeNextCodeFn(taken);
let filesChanged = 0;
let placeholdersReplaced = 0;
for (const file of files) {
let text;
try {
text = await fs.readFile(file, 'utf8');
} catch (err) {
console.warn(`! Failed to read ${file}: ${err.message}`);
continue;
}
if (!text.includes(PLACEHOLDER)) continue;
let countInFile = 0;
const updated = text.replaceAll(PLACEHOLDER, () => {
countInFile++;
return nextCode();
});
if (countInFile > 0) {
placeholdersReplaced += countInFile;
filesChanged++;
console.log(`${dryRun ? '[dry]' : '[write]'} ${file}${countInFile} replacement(s)`);
if (!dryRun) {
try {
await fs.writeFile(file, updated, 'utf8');
} catch (err) {
console.warn(`! Failed to write ${file}: ${err.message}`);
}
}
}
}
console.log(`Done. Files changed: ${filesChanged}, placeholders replaced: ${placeholdersReplaced}.`);
})().catch(err => {
console.error(err);
process.exit(1);
});
+110
View File
@@ -0,0 +1,110 @@
import fs from 'node:fs/promises';
import { createHash } from 'node:crypto';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import YAML from 'yaml';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function sha512Base64(filePath) {
const buf = await fs.readFile(filePath);
const h = createHash('sha512');
h.update(buf);
return h.digest('base64');
}
async function fileSize(filePath) {
const st = await fs.stat(filePath);
return st.size;
}
async function fixOneYaml(ymlPath, distDir) {
let raw;
try {
raw = await fs.readFile(ymlPath, 'utf8');
} catch (e) {
console.error(`✗ Cannot read ${ymlPath}:`, e.message);
return;
}
let doc;
try {
doc = YAML.parse(raw);
} catch (e) {
console.error(`✗ Cannot parse YAML ${ymlPath}:`, e.message);
return;
}
if (!doc || !Array.isArray(doc.files)) {
console.warn(`! ${path.basename(ymlPath)} has no 'files' array — skipped.`);
return;
}
let changed = false;
// Update each files[i].sha512 and files[i].size based on files[i].url
for (const entry of doc.files) {
if (!entry?.url) continue;
const target = path.resolve(distDir, entry.url);
try {
const [hash, size] = await Promise.all([sha512Base64(target), fileSize(target)]);
if (entry.sha512 !== hash || entry.size !== size) {
console.log(`${path.basename(ymlPath)}: refresh ${entry.url}`);
entry.sha512 = hash;
entry.size = size;
changed = true;
}
} catch (e) {
console.warn(
`! Missing or unreadable file for ${entry.url} (referenced by ${path.basename(ymlPath)}): ${e.message}`
);
}
}
// Update top-level sha512 for the primary "path" file if present
if (doc.path) {
const primary = path.resolve(distDir, doc.path);
try {
const hash = await sha512Base64(primary);
if (doc.sha512 !== hash) {
console.log(`${path.basename(ymlPath)}: refresh top-level sha512 for path=${doc.path}`);
doc.sha512 = hash;
changed = true;
}
} catch (e) {
console.warn(`! Primary 'path' file not found for ${path.basename(ymlPath)}: ${doc.path} (${e.message})`);
}
}
if (changed) {
const out = YAML.stringify(doc);
await fs.writeFile(ymlPath, out, 'utf8');
console.log(`✓ Updated ${path.basename(ymlPath)}`);
} else {
console.log(`= No changes for ${path.basename(ymlPath)}`);
}
}
async function main() {
const distDir = path.resolve(process.argv[2] ?? path.join(__dirname, '..', 'app', 'dist'));
const entries = await fs.readdir(distDir);
const ymls = entries.filter(f => f.toLowerCase().endsWith('.yml'));
if (ymls.length === 0) {
console.warn(`No .yml files found in ${distDir}`);
return;
}
console.log(`Scanning ${distDir}`);
for (const y of ymls) {
await fixOneYaml(path.join(distDir, y), distDir);
}
}
main().catch(err => {
console.error(err);
process.exit(1);
});
+3 -2
View File
@@ -6,7 +6,7 @@ const { getFiles } = require('./helpers');
const readFilePromise = promisify(fs.readFile);
const translationRegex = /_t\(\s*['"]([^'"]+)['"]\s*,\s*\{\s*defaultMessage\s*:\s*['"]([^'"]+)['"]\s*\}/g;
const translationRegex = /_t\(\s*['"]([^'"]+)['"]\s*,\s*\{\s*defaultMessage\s*:\s*(?:'([^'\\]*(?:\\.[^'\\]*)*)'|"([^"\\]*(?:\\.[^"\\]*)*)"|\`([^`\\]*(?:\\.[^`\\]*)*(?:\{[^}]*\}[^`\\]*(?:\\.[^`\\]*)*)*)\`)(?:\s*,\s*[^}]*)*\s*\}/g;
/**
* @param {string} file
@@ -20,7 +20,8 @@ async function extractTranslationsFromFile(file) {
let match;
while ((match = translationRegex.exec(content)) !== null) {
const [_, key, defaultText] = match;
const [_, key, singleQuotedText, doubleQuotedText, templateLiteral] = match;
const defaultText = singleQuotedText || doubleQuotedText || templateLiteral;
translations[key] = defaultText;
}
@@ -119,4 +119,17 @@ describe('Add connection', () => {
cy.contains('Export connections').click();
cy.themeshot('export-connections');
});
it('configure LLM provider', () => {
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Settings').click();
cy.contains('AI').click();
cy.testid('AiSupportedProvidersInfo_add_OpenRouter').click();
cy.testid('AiProviderCard_apikey_OpenRouter').clear().type('xxx');
cy.testid('AiProviderCard_testButton_OpenRouter').click();
cy.testid('AiProviderCard_statusValid_OpenRouter').should('exist');
cy.testid('AiProviderCard_editButton_OpenRouter').click();
cy.wait(1000);
cy.themeshot('llm-providers-settings');
});
});
+8 -21
View File
@@ -277,6 +277,14 @@ describe('Data browser data', () => {
cy.testid('CommandPalette_main').themeshot('command-palette', { padding: 50 });
});
it('About window', () => {
cy.contains('Connections');
cy.testid('WidgetIconPanel_menu').click();
cy.contains('Help').click();
cy.contains('About').click();
cy.testid('ModalBase_window').themeshot('about-window', { padding: 50 });
});
it('Show map', () => {
cy.contains('Postgres-connection').click();
cy.contains('PgGeoData').click();
@@ -381,27 +389,6 @@ describe('Data browser data', () => {
cy.themeshot('compare-database-settings');
});
it('Database chat', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_databaseChat').click();
cy.wait(1000);
cy.get('body').realType('find most popular artist');
cy.get('body').realPress('{enter}');
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 20000 }).click();
cy.wait(20000);
// cy.contains('Iron Maiden');
cy.themeshot('database-chat');
// cy.testid('DatabaseChatTab_promptInput').click();
// cy.get('body').realType('I need top 10 songs with the biggest income');
// cy.get('body').realPress('{enter}');
// cy.contains('Hot Girl', { timeout: 20000 });
// cy.wait(1000);
// cy.themeshot('database-chat');
});
it('Modify data', () => {
// TODO FIX: delete references cascade not working
cy.contains('MySql-connection').click();
+57
View File
@@ -109,4 +109,61 @@ describe('Charts', () => {
cy.contains('Compare database');
cy.themeshot('new-object-window');
});
it.only('Database chat - charts', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_databaseChat').click();
cy.wait(1000);
cy.get('body').realType('show me chart of most popular genres');
cy.get('body').realPress('{enter}');
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.testid('chart-canvas', { timeout: 30000 }).should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
cy.themeshot('database-chat-chart');
});
it('Database chat', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_databaseChat').click();
cy.wait(1000);
cy.get('body').realType('find most popular artist');
cy.get('body').realPress('{enter}');
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.wait(30000);
// cy.contains('Iron Maiden');
cy.themeshot('database-chat');
// cy.testid('DatabaseChatTab_promptInput').click();
// cy.get('body').realType('I need top 10 songs with the biggest income');
// cy.get('body').realPress('{enter}');
// cy.contains('Hot Girl', { timeout: 20000 });
// cy.wait(1000);
// cy.themeshot('database-chat');
});
it('Explain query error', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_query').click();
cy.wait(1000);
cy.get('body').realType('select * from Invoice2');
cy.contains('Execute').click();
cy.testid('MessageViewRow-explainErrorButton-1').click();
cy.testid('ChatCodeRenderer_useSqlButton', { timeout: 30000 });
cy.themeshot('explain-query-error');
// cy.testid('TabsPanel_buttonNewObject').click();
// cy.testid('NewObjectModal_databaseChat').click();
// cy.wait(1000);
// cy.get('body').realType('show me chart of most popular genres');
// cy.get('body').realPress('{enter}');
// cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
// cy.wait(5000);
// cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
// cy.themeshot('database-chat-chart');
});
});
+29
View File
@@ -21,14 +21,17 @@ describe('Team edition tests', () => {
cy.testid('AdminMenuWidget_itemConnections').click();
cy.contains('New connection').click();
cy.testid('ConnectionDriverFields_connectionType').select('PostgreSQL');
cy.contains('not granted').should('not.exist');
cy.themeshot('connection-administration');
cy.testid('AdminMenuWidget_itemRoles').click();
cy.contains('logged-user').click();
cy.contains('not granted').should('not.exist');
cy.themeshot('role-administration');
cy.testid('AdminMenuWidget_itemUsers').click();
cy.contains('New user').click();
cy.contains('not granted').should('not.exist');
cy.themeshot('user-administration');
cy.testid('AdminMenuWidget_itemAuthentication').click();
@@ -36,6 +39,7 @@ describe('Team edition tests', () => {
cy.contains('Use database login').click();
cy.contains('Add authentication').click();
cy.contains('OAuth 2.0').click();
cy.contains('not granted').should('not.exist');
cy.themeshot('authentication-administration');
});
@@ -119,4 +123,29 @@ describe('Team edition tests', () => {
cy.contains('Exporting query').click();
cy.themeshot('auditlog');
});
it('Edit database permissions', () => {
cy.testid('LoginPage_linkAdmin').click();
cy.testid('LoginPage_password').type('adminpwd');
cy.testid('LoginPage_submitLogin').click();
cy.testid('AdminMenuWidget_itemRoles').click();
cy.testid('AdminRolesTab_table').contains('superadmin').click();
cy.testid('AdminRolesTab_databases').click();
cy.testid('AdminDatabasesPermissionsGrid_addButton').click();
cy.testid('AdminDatabasesPermissionsGrid_addButton').click();
cy.testid('AdminDatabasesPermissionsGrid_addButton').click();
cy.testid('AdminListOrRegexEditor_1_regexInput').type('^Chinook[\\d]*$');
cy.testid('AdminListOrRegexEditor_2_listSwitch').click();
cy.testid('AdminListOrRegexEditor_2_listInput').type('Nortwind\nSales');
cy.testid('AdminDatabasesPermissionsGrid_roleSelect_0').select('-2');
cy.testid('AdminDatabasesPermissionsGrid_roleSelect_1').select('-3');
cy.testid('AdminDatabasesPermissionsGrid_roleSelect_2').select('-4');
cy.contains('not granted').should('not.exist');
cy.themeshot('database-permissions');
});
});
+1 -1
View File
@@ -37,7 +37,7 @@ services:
- "16009:5556"
mongo:
image: mongo:4.0.12
image: mongo:4.4.29
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
@@ -118,6 +118,31 @@ describe('Alter table', () => {
})
);
test.each(engines.filter(i => i.supportTableComments).map(engine => [engine.label, engine]))(
'Add comment to table - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(engine, conn, driver, tbl => {
tbl.objectComment = 'Added table comment';
});
})
);
test.each(engines.filter(i => i.supportColumnComments).map(engine => [engine.label, engine]))(
'Add comment to column - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(engine, conn, driver, tbl => {
tbl.columns.push({
columnName: 'added',
columnComment: 'Added column comment',
dataType: 'int',
pairingId: crypto.randomUUID(),
notNull: false,
autoIncrement: false,
});
});
})
);
test.each(
createEnginesColumnsSource(engines.filter(x => !x.skipDropColumn)).filter(
([_label, col, engine]) => !engine.skipPkDrop || !col.endsWith('_pk')
+73
View File
@@ -49,6 +49,32 @@ class StreamHandler {
}
}
class BinaryTestStreamHandler {
constructor(resolve, reject, expectedValue) {
this.resolve = resolve;
this.reject = reject;
this.expectedValue = expectedValue;
this.rowsReceived = [];
}
row(row) {
try {
this.rowsReceived.push(row);
if (this.expectedValue) {
expect(row).toEqual(this.expectedValue);
}
} catch (error) {
this.reject(error);
return;
}
}
recordset(columns) {}
done(result) {
this.resolve(this.rowsReceived);
}
info(msg) {}
}
function executeStreamItem(driver, conn, sql) {
return new Promise(resolve => {
const handler = new StreamHandler(resolve);
@@ -223,4 +249,51 @@ describe('Query', () => {
expect(row[keys[0]] == 1).toBeTruthy();
})
);
test.each(engines.filter(x => x.binaryDataType).map(engine => [engine.label, engine]))(
'Binary - %s',
testWrapper(async (dbhan, driver, engine) => {
await runCommandOnDriver(dbhan, driver, dmp =>
dmp.createTable({
pureName: 't1',
columns: [
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
{ columnName: 'val', dataType: engine.binaryDataType },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
})
);
const structure = await driver.analyseFull(dbhan);
const table = structure.tables.find(x => x.pureName == 't1');
const dmp = driver.createDumper();
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES (%v)", {
$binary: { base64: 'iVBORw0KWgo=' },
});
await driver.query(dbhan, dmp.s, {discardResult: true});
const dmp2 = driver.createDumper();
dmp2.put('SELECT ~val FROM ~t1');
const res = await driver.query(dbhan, dmp2.s);
const row = res.rows[0];
const keys = Object.keys(row);
expect(keys.length).toEqual(1);
expect(row[keys[0]]).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
const res2 = await driver.readQuery(dbhan, dmp2.s);
const rows = await Array.fromAsync(res2);
const rowsVal = rows.filter(r => r.val != null);
expect(rowsVal.length).toEqual(1);
expect(rowsVal[0].val).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
const res3 = await new Promise((resolve, reject) => {
const handler = new BinaryTestStreamHandler(resolve, reject, {val: {$binary: {base64: 'iVBORw0KWgo='}}});
driver.stream(dbhan, dmp2.s, handler);
});
})
);
});
@@ -64,6 +64,40 @@ describe('Table create', () => {
})
);
test.each(
engines.filter(i => i.supportTableComments || i.supportColumnComments).map(engine => [engine.label, engine])
)(
'Simple table with comment - %s',
testWrapper(async (conn, driver, engine) => {
await testTableCreate(engine, conn, driver, {
...(engine.supportTableComments && {
schemaName: 'dbo',
objectComment: 'table comment',
}),
...(engine.defaultSchemaName && {
schemaName: engine.defaultSchemaName,
}),
columns: [
{
columnName: 'col1',
dataType: 'int',
pureName: 'tested',
...(engine.skipNullability ? {} : { notNull: true }),
...(engine.supportColumnComments && {
columnComment: 'column comment',
}),
...(engine.defaultSchemaName && {
schemaName: engine.defaultSchemaName,
}),
},
],
primaryKey: {
columns: [{ columnName: 'col1' }],
},
});
})
);
test.each(engines.filter(x => !x.skipIndexes).map(engine => [engine.label, engine]))(
'Table with index - %s',
testWrapper(async (conn, driver, engine) => {
+13 -6
View File
@@ -44,6 +44,7 @@ const mysqlEngine = {
supportRenameSqlObject: false,
dbSnapshotBySeconds: true,
dumpFile: 'data/chinook-mysql.sql',
binaryDataType: 'blob',
dumpChecks: [
{
sql: 'select count(*) as res from genre',
@@ -216,6 +217,7 @@ const postgreSqlEngine = {
supportSchemas: true,
supportRenameSqlObject: true,
defaultSchemaName: 'public',
binaryDataType: 'bytea',
dumpFile: 'data/chinook-postgre.sql',
dumpChecks: [
{
@@ -443,7 +445,10 @@ const sqlServerEngine = {
supportSchemas: true,
supportRenameSqlObject: true,
defaultSchemaName: 'dbo',
supportTableComments: true,
supportColumnComments: true,
// skipSeparateSchemas: true,
binaryDataType: 'varbinary(100)',
triggers: [
{
testName: 'triggers before each row',
@@ -504,6 +509,7 @@ const sqliteEngine = {
},
},
],
binaryDataType: 'blob',
};
const libsqlFileEngine = {
@@ -617,6 +623,7 @@ const oracleEngine = {
},
},
],
binaryDataType: 'blob',
};
/** @type {import('dbgate-types').TestEngineInfo} */
@@ -750,18 +757,18 @@ const enginesOnCi = [
const enginesOnLocal = [
// all engines, which would be run on local test
// cassandraEngine,
// mysqlEngine,
//mysqlEngine,
// mariaDbEngine,
// postgreSqlEngine,
// sqlServerEngine,
// sqliteEngine,
//postgreSqlEngine,
//sqlServerEngine,
sqliteEngine,
// cockroachDbEngine,
// clickhouseEngine,
// libsqlFileEngine,
// libsqlWsEngine,
// oracleEngine,
//oracleEngine,
// duckdbEngine,
firebirdEngine,
//firebirdEngine,
];
/** @type {import('dbgate-types').TestEngineInfo[] & Record<string, import('dbgate-types').TestEngineInfo>} */
+4 -2
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "6.6.1-beta.10",
"version": "6.6.12-premium-beta.5",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -52,6 +52,7 @@
"generatePadFile": "node generatePadFile",
"fillPackagedPlugins": "node fillPackagedPlugins",
"resetPackagedPlugins": "node resetPackagedPlugins",
"fixYmlHashes": "cd common && yarn init -y && yarn add yaml -W && cd .. && node common/fixYmlHashes.js app/dist",
"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",
"copy:packer:build": "copyfiles packages/api/dist/* packer/build -f && copyfiles packages/web/public/* packer/build -u 2 && copyfiles \"packages/web/public/**/*\" packer/build -u 2 && copyfiles \"plugins/dist/**/*\" packer/build/plugins -u 2 && copyfiles packer/install-packages.sh packer/build -f && yarn install:drivers:packer",
@@ -72,7 +73,8 @@
"translations:extract": "node common/translations-cli/index.js extract",
"translations:add-missing": "node common/translations-cli/index.js add-missing",
"translations:remove-unused": "node common/translations-cli/index.js remove-unused",
"translations:check": "node common/translations-cli/index.js check"
"translations:check": "node common/translations-cli/index.js check",
"errors": "node common/assign-dbgm-codes.mjs ."
},
"dependencies": {
"concurrently": "^5.1.0",
+1
View File
@@ -2,6 +2,7 @@ DEVMODE=1
SHELL_SCRIPTING=1
ALLOW_DBGATE_PRIVATE_CLOUD=1
DEVWEB=1
# LOCAL_AUTH_PROXY=1
# LOCAL_AI_GATEWAY=true
# REDIRECT_TO_DBGATE_CLOUD_LOGIN=1
+2 -2
View File
@@ -31,7 +31,7 @@
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.5",
"dbgate-query-splitter": "^4.11.7",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"debug": "^4.3.4",
@@ -56,7 +56,7 @@
"ncp": "^2.0.0",
"node-cron": "^2.0.3",
"on-finished": "^2.4.1",
"pinomin": "^1.0.4",
"pinomin": "^1.0.5",
"portfinder": "^1.0.28",
"rimraf": "^3.0.0",
"semver": "^7.6.3",
+6
View File
@@ -10,7 +10,13 @@ function getTokenSecret() {
return tokenSecret;
}
function getStaticTokenSecret() {
// TODO static not fixed
return '14813c43-a91b-4ad1-9dcd-a81bd7dbb05f';
}
module.exports = {
getTokenLifetime,
getTokenSecret,
getStaticTokenSecret,
};
+19 -2
View File
@@ -10,6 +10,7 @@ const logger = getLogger('authProvider');
class AuthProviderBase {
amoid = 'none';
skipInList = false;
async login(login, password, options = undefined, req = undefined) {
return {
@@ -36,12 +37,28 @@ class AuthProviderBase {
return !!req?.user || !!req?.auth;
}
getCurrentPermissions(req) {
async getCurrentPermissions(req) {
const login = this.getCurrentLogin(req);
const permissions = process.env[`LOGIN_PERMISSIONS_${login}`];
return permissions || process.env.PERMISSIONS;
}
async checkCurrentConnectionPermission(req, conid) {
return true;
}
async getCurrentDatabasePermissions(req) {
return [];
}
async getCurrentTablePermissions(req) {
return [];
}
async getCurrentFilePermissions(req) {
return [];
}
getLoginPageConnections() {
return null;
}
@@ -94,7 +111,7 @@ class OAuthProvider extends AuthProviderBase {
payload = jwt.decode(id_token);
}
logger.info({ payload }, 'User payload returned from OAUTH');
logger.info({ payload }, 'DBGM-00002 User payload returned from OAUTH');
const login =
process.env.OAUTH_LOGIN_FIELD && payload && payload[process.env.OAUTH_LOGIN_FIELD]
+341 -219
View File
@@ -1,233 +1,99 @@
const fs = require('fs-extra');
const _ = require('lodash');
const path = require('path');
const { appdir } = require('../utility/directories');
const { appdir, filesdir } = require('../utility/directories');
const socket = require('../utility/socket');
const connections = require('./connections');
const {
loadPermissionsFromRequest,
loadFilePermissionsFromRequest,
hasPermission,
getFilePermissionRole,
} = require('../utility/hasPermission');
module.exports = {
folders_meta: true,
async folders() {
const folders = await fs.readdir(appdir());
return [
...folders.map(name => ({
name,
})),
];
},
createFolder_meta: true,
async createFolder({ folder }) {
const name = await this.getNewAppFolder({ name: folder });
await fs.mkdir(path.join(appdir(), name));
socket.emitChanged('app-folders-changed');
this.emitChangedDbApp(folder);
return name;
},
files_meta: true,
async files({ folder }) {
if (!folder) return [];
const dir = path.join(appdir(), folder);
getAllApps_meta: true,
async getAllApps({}, req) {
const dir = path.join(filesdir(), 'apps');
if (!(await fs.exists(dir))) return [];
const files = await fs.readdir(dir);
function fileType(ext, type) {
return files
.filter(name => name.endsWith(ext))
.map(name => ({
name: name.slice(0, -ext.length),
label: path.parse(name.slice(0, -ext.length)).base,
type,
}));
}
return [
...fileType('.command.sql', 'command.sql'),
...fileType('.query.sql', 'query.sql'),
...fileType('.config.json', 'config.json'),
];
},
async emitChangedDbApp(folder) {
const used = await this.getUsedAppFolders();
if (used.includes(folder)) {
socket.emitChanged('used-apps-changed');
}
},
refreshFiles_meta: true,
async refreshFiles({ folder }) {
socket.emitChanged('app-files-changed', { app: folder });
},
refreshFolders_meta: true,
async refreshFolders() {
socket.emitChanged(`app-folders-changed`);
},
deleteFile_meta: true,
async deleteFile({ folder, file, fileType }) {
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
socket.emitChanged('app-files-changed', { app: folder });
this.emitChangedDbApp(folder);
},
renameFile_meta: true,
async renameFile({ folder, file, newFile, fileType }) {
await fs.rename(
path.join(path.join(appdir(), folder), `${file}.${fileType}`),
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
);
socket.emitChanged('app-files-changed', { app: folder });
this.emitChangedDbApp(folder);
},
renameFolder_meta: true,
async renameFolder({ folder, newFolder }) {
const uniqueName = await this.getNewAppFolder({ name: newFolder });
await fs.rename(path.join(appdir(), folder), path.join(appdir(), uniqueName));
socket.emitChanged(`app-folders-changed`);
},
deleteFolder_meta: true,
async deleteFolder({ folder }) {
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', { app: folder });
socket.emitChanged('used-apps-changed');
},
async getNewAppFolder({ name }) {
if (!(await fs.exists(path.join(appdir(), name)))) return name;
let index = 2;
while (await fs.exists(path.join(appdir(), `${name}${index}`))) {
index += 1;
}
return `${name}${index}`;
},
getUsedAppFolders_meta: true,
async getUsedAppFolders() {
const list = await connections.list();
const apps = [];
for (const connection of list) {
for (const db of connection.databases || []) {
for (const key of _.keys(db || {})) {
if (key.startsWith('useApp:') && db[key]) {
apps.push(key.substring('useApp:'.length));
}
}
}
}
return _.intersection(_.uniq(apps), await fs.readdir(appdir()));
},
getUsedApps_meta: true,
async getUsedApps() {
const apps = await this.getUsedAppFolders();
const res = [];
const loadedPermissions = await loadPermissionsFromRequest(req);
const filePermissions = await loadFilePermissionsFromRequest(req);
for (const folder of apps) {
res.push(await this.loadApp({ folder }));
}
return res;
},
// getAppsForDb_meta: true,
// async getAppsForDb({ conid, database }) {
// const connection = await connections.get({ conid });
// if (!connection) return [];
// const db = (connection.databases || []).find(x => x.name == database);
// const apps = [];
// const res = [];
// if (db) {
// for (const key of _.keys(db || {})) {
// if (key.startsWith('useApp:') && db[key]) {
// apps.push(key.substring('useApp:'.length));
// }
// }
// }
// for (const folder of apps) {
// res.push(await this.loadApp({ folder }));
// }
// return res;
// },
loadApp_meta: true,
async loadApp({ folder }) {
const res = {
queries: [],
commands: [],
name: folder,
};
const dir = path.join(appdir(), folder);
if (await fs.exists(dir)) {
const files = await fs.readdir(dir);
async function processType(ext, field) {
for (const file of files) {
if (file.endsWith(ext)) {
res[field].push({
name: file.slice(0, -ext.length),
sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
});
}
}
for (const file of await fs.readdir(dir)) {
if (!hasPermission(`all-disk-files`, loadedPermissions)) {
const role = getFilePermissionRole('apps', file, filePermissions);
if (role == 'deny') continue;
}
const content = await fs.readFile(path.join(dir, file), { encoding: 'utf-8' });
const appJson = JSON.parse(content);
// const app = {
// appid: file,
// name: appJson.applicationName,
// usageRules: appJson.usageRules || [],
// icon: appJson.applicationIcon || 'img app',
// color: appJson.applicationColor,
// queries: Object.values(appJson.files || {})
// .filter(x => x.type == 'query')
// .map(x => ({
// name: x.label,
// sql: x.sql,
// })),
// commands: Object.values(appJson.files || {})
// .filter(x => x.type == 'command')
// .map(x => ({
// name: x.label,
// sql: x.sql,
// })),
// virtualReferences: appJson.virtualReferences,
// dictionaryDescriptions: appJson.dictionaryDescriptions,
// };
const app = {
...appJson,
appid: file,
};
await processType('.command.sql', 'commands');
await processType('.query.sql', 'queries');
res.push(app);
}
try {
res.virtualReferences = JSON.parse(
await fs.readFile(path.join(dir, 'virtual-references.config.json'), { encoding: 'utf-8' })
);
} catch (err) {
res.virtualReferences = [];
}
try {
res.dictionaryDescriptions = JSON.parse(
await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
);
} catch (err) {
res.dictionaryDescriptions = [];
}
return res;
},
async saveConfigFile(appFolder, filename, filterFunc, newItem) {
const file = path.join(appdir(), appFolder, filename);
let json;
try {
json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
} catch (err) {
json = [];
createAppFromDb_meta: true,
async createAppFromDb({ appName, server, database }, req) {
const appdir = path.join(filesdir(), 'apps');
if (!fs.existsSync(appdir)) {
await fs.mkdir(appdir);
}
if (filterFunc) {
json = json.filter(filterFunc);
const appId = _.kebabCase(appName);
let suffix = undefined;
while (fs.existsSync(path.join(appdir, `${appId}${suffix || ''}`))) {
if (!suffix) suffix = 2;
else suffix++;
}
const finalAppId = `${appId}${suffix || ''}`;
json = [...json, newItem];
const appJson = {
applicationName: appName,
usageRules: [
{
serverHostsList: server,
databaseNamesList: database,
},
],
};
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
await fs.writeFile(path.join(appdir, `${finalAppId}`), JSON.stringify(appJson, undefined, 2));
socket.emitChanged('app-files-changed', { app: appFolder });
socket.emitChanged('used-apps-changed');
socket.emitChanged(`files-changed`, { folder: 'apps' });
return finalAppId;
},
saveVirtualReference_meta: true,
async saveVirtualReference({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
await this.saveConfigFile(
appFolder,
'virtual-references.config.json',
async saveVirtualReference({ appid, schemaName, pureName, refSchemaName, refTableName, columns }) {
await this.saveConfigItem(
appid,
'virtualReferences',
columns.length == 1
? x =>
!(
@@ -245,14 +111,17 @@ module.exports = {
columns,
}
);
socket.emitChanged(`files-changed`, { folder: 'apps' });
return true;
},
saveDictionaryDescription_meta: true,
async saveDictionaryDescription({ appFolder, pureName, schemaName, expression, columns, delimiter }) {
await this.saveConfigFile(
appFolder,
'dictionary-descriptions.config.json',
async saveDictionaryDescription({ appid, pureName, schemaName, expression, columns, delimiter }) {
await this.saveConfigItem(
appid,
'dictionaryDescriptions',
x => !(x.schemaName == schemaName && x.pureName == pureName),
{
schemaName,
@@ -263,18 +132,271 @@ module.exports = {
}
);
socket.emitChanged(`files-changed`, { folder: 'apps' });
return true;
},
createConfigFile_meta: true,
async createConfigFile({ appFolder, fileName, content }) {
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', { app: appFolder });
socket.emitChanged('used-apps-changed');
return true;
async saveConfigItem(appid, fieldName, filterFunc, newItem) {
const file = path.join(filesdir(), 'apps', appid);
const appJson = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
let json = appJson[fieldName] || [];
if (filterFunc) {
json = json.filter(filterFunc);
}
return false;
json = [...json, newItem];
await fs.writeFile(
file,
JSON.stringify(
{
...appJson,
[fieldName]: json,
},
undefined,
2
)
);
socket.emitChanged('files-changed', { folder: 'apps' });
},
// folders_meta: true,
// async folders() {
// const folders = await fs.readdir(appdir());
// return [
// ...folders.map(name => ({
// name,
// })),
// ];
// },
// createFolder_meta: true,
// async createFolder({ folder }) {
// const name = await this.getNewAppFolder({ name: folder });
// await fs.mkdir(path.join(appdir(), name));
// socket.emitChanged('app-folders-changed');
// this.emitChangedDbApp(folder);
// return name;
// },
// files_meta: true,
// async files({ folder }) {
// if (!folder) return [];
// const dir = path.join(appdir(), folder);
// if (!(await fs.exists(dir))) return [];
// const files = await fs.readdir(dir);
// function fileType(ext, type) {
// return files
// .filter(name => name.endsWith(ext))
// .map(name => ({
// name: name.slice(0, -ext.length),
// label: path.parse(name.slice(0, -ext.length)).base,
// type,
// }));
// }
// return [
// ...fileType('.command.sql', 'command.sql'),
// ...fileType('.query.sql', 'query.sql'),
// ...fileType('.config.json', 'config.json'),
// ];
// },
// async emitChangedDbApp(folder) {
// const used = await this.getUsedAppFolders();
// if (used.includes(folder)) {
// socket.emitChanged('used-apps-changed');
// }
// },
// refreshFiles_meta: true,
// async refreshFiles({ folder }) {
// socket.emitChanged('app-files-changed', { app: folder });
// },
// refreshFolders_meta: true,
// async refreshFolders() {
// socket.emitChanged(`app-folders-changed`);
// },
// deleteFile_meta: true,
// async deleteFile({ folder, file, fileType }) {
// await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
// socket.emitChanged('app-files-changed', { app: folder });
// this.emitChangedDbApp(folder);
// },
// renameFile_meta: true,
// async renameFile({ folder, file, newFile, fileType }) {
// await fs.rename(
// path.join(path.join(appdir(), folder), `${file}.${fileType}`),
// path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
// );
// socket.emitChanged('app-files-changed', { app: folder });
// this.emitChangedDbApp(folder);
// },
// renameFolder_meta: true,
// async renameFolder({ folder, newFolder }) {
// const uniqueName = await this.getNewAppFolder({ name: newFolder });
// await fs.rename(path.join(appdir(), folder), path.join(appdir(), uniqueName));
// socket.emitChanged(`app-folders-changed`);
// },
// deleteFolder_meta: true,
// async deleteFolder({ folder }) {
// 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', { app: folder });
// socket.emitChanged('used-apps-changed');
// },
// async getNewAppFolder({ name }) {
// if (!(await fs.exists(path.join(appdir(), name)))) return name;
// let index = 2;
// while (await fs.exists(path.join(appdir(), `${name}${index}`))) {
// index += 1;
// }
// return `${name}${index}`;
// },
// getUsedAppFolders_meta: true,
// async getUsedAppFolders() {
// const list = await connections.list();
// const apps = [];
// for (const connection of list) {
// for (const db of connection.databases || []) {
// for (const key of _.keys(db || {})) {
// if (key.startsWith('useApp:') && db[key]) {
// apps.push(key.substring('useApp:'.length));
// }
// }
// }
// }
// return _.intersection(_.uniq(apps), await fs.readdir(appdir()));
// },
// // getAppsForDb_meta: true,
// // async getAppsForDb({ conid, database }) {
// // const connection = await connections.get({ conid });
// // if (!connection) return [];
// // const db = (connection.databases || []).find(x => x.name == database);
// // const apps = [];
// // const res = [];
// // if (db) {
// // for (const key of _.keys(db || {})) {
// // if (key.startsWith('useApp:') && db[key]) {
// // apps.push(key.substring('useApp:'.length));
// // }
// // }
// // }
// // for (const folder of apps) {
// // res.push(await this.loadApp({ folder }));
// // }
// // return res;
// // },
// loadApp_meta: true,
// async loadApp({ folder }) {
// const res = {
// queries: [],
// commands: [],
// name: folder,
// };
// const dir = path.join(appdir(), folder);
// if (await fs.exists(dir)) {
// const files = await fs.readdir(dir);
// async function processType(ext, field) {
// for (const file of files) {
// if (file.endsWith(ext)) {
// res[field].push({
// name: file.slice(0, -ext.length),
// sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
// });
// }
// }
// }
// await processType('.command.sql', 'commands');
// await processType('.query.sql', 'queries');
// }
// try {
// res.virtualReferences = JSON.parse(
// await fs.readFile(path.join(dir, 'virtual-references.config.json'), { encoding: 'utf-8' })
// );
// } catch (err) {
// res.virtualReferences = [];
// }
// try {
// res.dictionaryDescriptions = JSON.parse(
// await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
// );
// } catch (err) {
// res.dictionaryDescriptions = [];
// }
// return res;
// },
// async saveConfigFile(appFolder, filename, filterFunc, newItem) {
// const file = path.join(appdir(), appFolder, filename);
// let json;
// try {
// json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
// } catch (err) {
// json = [];
// }
// if (filterFunc) {
// json = json.filter(filterFunc);
// }
// json = [...json, newItem];
// await fs.writeFile(file, JSON.stringify(json, undefined, 2));
// socket.emitChanged('app-files-changed', { app: appFolder });
// socket.emitChanged('used-apps-changed');
// },
// saveDictionaryDescription_meta: true,
// async saveDictionaryDescription({ appFolder, pureName, schemaName, expression, columns, delimiter }) {
// await this.saveConfigFile(
// appFolder,
// 'dictionary-descriptions.config.json',
// x => !(x.schemaName == schemaName && x.pureName == pureName),
// {
// schemaName,
// pureName,
// expression,
// columns,
// delimiter,
// }
// );
// return true;
// },
// createConfigFile_meta: true,
// async createConfigFile({ appFolder, fileName, content }) {
// 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', { app: appFolder });
// socket.emitChanged('used-apps-changed');
// return true;
// }
// return false;
// },
};
+1 -1
View File
@@ -102,7 +102,7 @@ module.exports = {
...fileType('.matview.sql', 'matview.sql'),
];
} catch (err) {
logger.error(extractErrorLogData(err), 'Error reading archive files');
logger.error(extractErrorLogData(err), 'DBGM-00001 Error reading archive files');
return [];
}
},
+6 -3
View File
@@ -51,6 +51,7 @@ function authMiddleware(req, res, next) {
'/auth/oauth-token',
'/auth/login',
'/auth/redirect',
'/redirect',
'/stream',
'/storage/get-connections-for-login-page',
'/storage/set-admin-password',
@@ -99,7 +100,7 @@ function authMiddleware(req, res, next) {
return next();
}
logger.error(extractErrorLogData(err), 'Sending invalid token error');
logger.error(extractErrorLogData(err), 'DBGM-00098 Sending invalid token error');
return unauthorizedResponse(req, res, 'invalid token');
}
@@ -139,9 +140,9 @@ module.exports = {
const accessToken = jwt.sign(
{
login: 'superadmin',
permissions: await storage.loadSuperadminPermissions(),
roleId: -3,
licenseUid,
amoid: 'superadmin',
},
getTokenSecret(),
{
@@ -173,7 +174,9 @@ module.exports = {
getProviders_meta: true,
getProviders() {
return {
providers: getAuthProviders().map(x => x.toJson()),
providers: getAuthProviders()
.filter(x => !x.skipInList)
.map(x => x.toJson()),
default: getDefaultAuthProvider()?.amoid,
};
},
+26 -1
View File
@@ -8,6 +8,9 @@ const {
getCloudContent,
putCloudContent,
removeCloudCachedConnection,
getPromoWidgetData,
getPromoWidgetList,
getPromoWidgetPreview,
} = require('../utility/cloudIntf');
const connections = require('./connections');
const socket = require('../utility/socket');
@@ -45,7 +48,7 @@ module.exports = {
const resp = await callCloudApiGet('content-list');
return resp;
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting cloud content list');
logger.error(extractErrorLogData(err), 'DBGM-00099 Error getting cloud content list');
return [];
}
@@ -283,6 +286,28 @@ module.exports = {
return getAiGatewayServer();
},
premiumPromoWidget_meta: true,
async premiumPromoWidget() {
const data = await getPromoWidgetData();
if (data?.state != 'data') {
return null;
}
if (data.validTo && new Date().getTime() > new Date(data.validTo).getTime()) {
return null;
}
return data;
},
promoWidgetList_meta: true,
async promoWidgetList() {
return getPromoWidgetList();
},
promoWidgetPreview_meta: true,
async promoWidgetPreview({ campaign, variant }) {
return getPromoWidgetPreview(campaign, variant);
},
// chatStream_meta: {
// raw: true,
// method: 'post',
+8 -5
View File
@@ -3,7 +3,7 @@ const os = require('os');
const path = require('path');
const axios = require('axios');
const { datadir, getLogsFilePath } = require('../utility/directories');
const { hasPermission } = require('../utility/hasPermission');
const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
const socket = require('../utility/socket');
const _ = require('lodash');
const AsyncLock = require('async-lock');
@@ -46,7 +46,7 @@ module.exports = {
async get(_params, req) {
const authProvider = getAuthProviderFromReq(req);
const login = authProvider.getCurrentLogin(req);
const permissions = authProvider.getCurrentPermissions(req);
const permissions = await authProvider.getCurrentPermissions(req);
const isUserLoggedIn = authProvider.isUserLoggedIn(req);
const singleConid = authProvider.getSingleConnectionId(req);
@@ -280,7 +280,8 @@ module.exports = {
updateSettings_meta: true,
async updateSettings(values, req) {
if (!hasPermission(`settings/change`, req)) return false;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`settings/change`, loadedPermissions)) return false;
cachedSettingsValue = null;
const res = await lock.acquire('settings', async () => {
@@ -392,7 +393,8 @@ module.exports = {
exportConnectionsAndSettings_meta: true,
async exportConnectionsAndSettings(_params, req) {
if (!hasPermission(`admin/config`, req)) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`admin/config`, loadedPermissions)) {
throw new Error('Permission denied: admin/config');
}
@@ -416,7 +418,8 @@ module.exports = {
importConnectionsAndSettings_meta: true,
async importConnectionsAndSettings({ db }, req) {
if (!hasPermission(`admin/config`, req)) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`admin/config`, loadedPermissions)) {
throw new Error('Permission denied: admin/config');
}
+12 -11
View File
@@ -14,7 +14,7 @@ const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
const processArgs = require('../utility/processArgs');
const { safeJsonParse, getLogger, extractErrorLogData } = require('dbgate-tools');
const platformInfo = require('../utility/platformInfo');
const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission');
const { connectionHasPermission, testConnectionPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
const pipeForkLogs = require('../utility/pipeForkLogs');
const requireEngineDriver = require('../utility/requireEngineDriver');
const { getAuthProviderById } = require('../auth/authProvider');
@@ -116,12 +116,12 @@ function getPortalCollections() {
}
}
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'Using connections from ENV variables');
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'DBGM-00005 Using connections from ENV variables');
const noengine = connections.filter(x => !x.engine);
if (noengine.length > 0) {
logger.warn(
{ connections: noengine.map(x => x._id) },
'Invalid CONNECTIONS configuration, missing ENGINE for connection ID'
'DBGM-00006 Invalid CONNECTIONS configuration, missing ENGINE for connection ID'
);
}
return connections;
@@ -227,6 +227,7 @@ module.exports = {
list_meta: true,
async list(_params, req) {
const storage = require('./storage');
const loadedPermissions = await loadPermissionsFromRequest(req);
const storageConnections = await storage.connections(req);
if (storageConnections) {
@@ -234,9 +235,9 @@ module.exports = {
}
if (portalConnections) {
if (platformInfo.allowShellConnection) return portalConnections;
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, req));
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, loadedPermissions));
}
return (await this.datastore.find()).filter(x => connectionHasPermission(x, req));
return (await this.datastore.find()).filter(x => connectionHasPermission(x, loadedPermissions));
},
async getUsedEngines() {
@@ -375,7 +376,7 @@ module.exports = {
update_meta: true,
async update({ _id, values }, req) {
if (portalConnections) return;
testConnectionPermission(_id, req);
await testConnectionPermission(_id, req);
const res = await this.datastore.patch(_id, values);
socket.emitChanged('connection-list-changed');
return res;
@@ -392,7 +393,7 @@ module.exports = {
updateDatabase_meta: true,
async updateDatabase({ conid, database, values }, req) {
if (portalConnections) return;
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const conn = await this.datastore.get(conid);
let databases = (conn && conn.databases) || [];
if (databases.find(x => x.name == database)) {
@@ -410,7 +411,7 @@ module.exports = {
delete_meta: true,
async delete(connection, req) {
if (portalConnections) return;
testConnectionPermission(connection, req);
await testConnectionPermission(connection, req);
const res = await this.datastore.remove(connection._id);
socket.emitChanged('connection-list-changed');
return res;
@@ -452,7 +453,7 @@ module.exports = {
_id: '__model',
};
}
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.getCore({ conid, mask: true });
},
@@ -530,7 +531,7 @@ module.exports = {
socket.emit('got-volatile-token', { strmid, savedConId: conid, volatileConId: volatile._id });
return { success: true };
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting DB token');
logger.error(extractErrorLogData(err), 'DBGM-00100 Error getting DB token');
return { error: err.message };
}
},
@@ -546,7 +547,7 @@ module.exports = {
const resp = await authProvider.login(null, null, { conid: volatile._id }, req);
return resp;
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting DB token');
logger.error(extractErrorLogData(err), 'DBGM-00101 Error getting DB token');
return { error: err.message };
}
},
@@ -29,7 +29,17 @@ const generateDeploySql = require('../shell/generateDeploySql');
const { createTwoFilesPatch } = require('diff');
const diff2htmlPage = require('../utility/diff2htmlPage');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
const {
testConnectionPermission,
hasPermission,
loadPermissionsFromRequest,
loadTablePermissionsFromRequest,
getTablePermissionRole,
loadDatabasePermissionsFromRequest,
getDatabasePermissionRole,
getTablePermissionRoleLevelIndex,
testDatabaseRolePermission,
} = require('../utility/hasPermission');
const { MissingCredentialsError } = require('../utility/exceptions');
const pipeForkLogs = require('../utility/pipeForkLogs');
const crypto = require('crypto');
@@ -76,7 +86,7 @@ module.exports = {
handle_error(conid, database, props) {
const { error } = props;
logger.error(`Error in database connection ${conid}, database ${database}: ${error}`);
logger.error(`DBGM-00102 Error in database connection ${conid}, database ${database}: ${error}`);
if (props?.msgid) {
const [resolve, reject] = this.requests[props?.msgid];
reject(error);
@@ -144,7 +154,7 @@ module.exports = {
handle_copyStreamError(conid, database, { copyStreamError }) {
const { progressName } = copyStreamError;
const { runid } = progressName;
logger.error(`Error in database connection ${conid}, database ${database}: ${copyStreamError}`);
logger.error(`DBGM-00103 Error in database connection ${conid}, database ${database}: ${copyStreamError}`);
socket.emit(`runner-done-${runid}`);
},
@@ -193,7 +203,7 @@ module.exports = {
if (newOpened.disconnected) return;
const funcName = `handle_${msgtype}`;
if (!this[funcName]) {
logger.error(`Unknown message type ${msgtype} from subprocess databaseConnectionProcess`);
logger.error(`DBGM-00104 Unknown message type ${msgtype} from subprocess databaseConnectionProcess`);
return;
}
@@ -204,7 +214,7 @@ module.exports = {
this.close(conid, database, false);
});
subprocess.on('error', err => {
logger.error(extractErrorLogData(err), 'Error in database connection subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00114 Error in database connection subprocess');
if (newOpened.disconnected) return;
this.close(conid, database, false);
});
@@ -226,7 +236,7 @@ module.exports = {
try {
conn.subprocess.send({ msgid, ...message });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error sending request do process');
logger.error(extractErrorLogData(err), 'DBGM-00115 Error sending request do process');
this.close(conn.conid, conn.database);
}
});
@@ -235,8 +245,8 @@ module.exports = {
queryData_meta: true,
async queryData({ conid, database, sql }, req) {
testConnectionPermission(conid, req);
logger.info({ conid, database, sql }, 'Processing query');
await testConnectionPermission(conid, req);
logger.info({ conid, database, sql }, 'DBGM-00007 Processing query');
const opened = await this.ensureOpened(conid, database);
// if (opened && opened.status && opened.status.name == 'error') {
// return opened.status;
@@ -247,7 +257,7 @@ module.exports = {
sqlSelect_meta: true,
async sqlSelect({ conid, database, select, auditLogSessionGroup }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(
opened,
@@ -282,8 +292,10 @@ module.exports = {
runScript_meta: true,
async runScript({ conid, database, sql, useTransaction, logMessage }, req) {
testConnectionPermission(conid, req);
logger.info({ conid, database, sql }, 'Processing script');
const loadedPermissions = await loadPermissionsFromRequest(req);
await testConnectionPermission(conid, req, loadedPermissions);
await testDatabaseRolePermission(conid, database, 'run_script', req);
logger.info({ conid, database, sql }, 'DBGM-00008 Processing script');
const opened = await this.ensureOpened(conid, database);
sendToAuditLog(req, {
category: 'dbop',
@@ -303,8 +315,8 @@ module.exports = {
runOperation_meta: true,
async runOperation({ conid, database, operation, useTransaction }, req) {
testConnectionPermission(conid, req);
logger.info({ conid, database, operation }, 'Processing operation');
await testConnectionPermission(conid, req);
logger.info({ conid, database, operation }, 'DBGM-00009 Processing operation');
sendToAuditLog(req, {
category: 'dbop',
@@ -325,7 +337,7 @@ module.exports = {
collectionData_meta: true,
async collectionData({ conid, database, options, auditLogSessionGroup }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(
opened,
@@ -356,7 +368,7 @@ module.exports = {
},
async loadDataCore(msgtype, { conid, database, ...args }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype, ...args });
if (res.errorMessage) {
@@ -371,7 +383,7 @@ module.exports = {
schemaList_meta: true,
async schemaList({ conid, database }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('schemaList', { conid, database });
},
@@ -383,43 +395,43 @@ module.exports = {
loadKeys_meta: true,
async loadKeys({ conid, database, root, filter, limit }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('loadKeys', { conid, database, root, filter, limit });
},
scanKeys_meta: true,
async scanKeys({ conid, database, root, pattern, cursor, count }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('scanKeys', { conid, database, root, pattern, cursor, count });
},
exportKeys_meta: true,
async exportKeys({ conid, database, options }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('exportKeys', { conid, database, options });
},
loadKeyInfo_meta: true,
async loadKeyInfo({ conid, database, key }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('loadKeyInfo', { conid, database, key });
},
loadKeyTableRange_meta: true,
async loadKeyTableRange({ conid, database, key, cursor, count }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
},
loadFieldValues_meta: true,
async loadFieldValues({ conid, database, schemaName, pureName, field, search, dataType }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search, dataType });
},
callMethod_meta: true,
async callMethod({ conid, database, method, args }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
return this.loadDataCore('callMethod', { conid, database, method, args });
// const opened = await this.ensureOpened(conid, database);
@@ -432,7 +444,8 @@ module.exports = {
updateCollection_meta: true,
async updateCollection({ conid, database, changeSet }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
if (res.errorMessage) {
@@ -443,6 +456,44 @@ module.exports = {
return res.result || null;
},
saveTableData_meta: true,
async saveTableData({ conid, database, changeSet }, req) {
await testConnectionPermission(conid, req);
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const tablePermissions = await loadTablePermissionsFromRequest(req);
const fieldsAndRoles = [
[changeSet.inserts, 'create_update_delete'],
[changeSet.deletes, 'create_update_delete'],
[changeSet.updates, 'update_only'],
];
for (const [operations, requiredRole] of fieldsAndRoles) {
for (const operation of operations) {
const role = getTablePermissionRole(
conid,
database,
'tables',
operation.schemaName,
operation.pureName,
tablePermissions,
databasePermissions
);
if (getTablePermissionRoleLevelIndex(role) < getTablePermissionRoleLevelIndex(requiredRole)) {
throw new Error('DBGM-00262 Permission not granted');
}
}
}
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'saveTableData', changeSet });
if (res.errorMessage) {
return {
errorMessage: res.errorMessage,
};
}
return res.result || null;
},
status_meta: true,
async status({ conid, database }, req) {
if (!conid) {
@@ -451,7 +502,7 @@ module.exports = {
message: 'No connection',
};
}
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
return {
@@ -474,14 +525,14 @@ module.exports = {
ping_meta: true,
async ping({ conid, database }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
let existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
try {
existing.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error pinging DB connection');
logger.error(extractErrorLogData(err), 'DBGM-00116 Error pinging DB connection');
this.close(conid, database);
return {
@@ -502,7 +553,7 @@ module.exports = {
refresh_meta: true,
async refresh({ conid, database, keepOpen }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
if (!keepOpen) this.close(conid, database);
await this.ensureOpened(conid, database);
@@ -516,7 +567,7 @@ module.exports = {
return { status: 'ok' };
}
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const conn = await this.ensureOpened(conid, database);
conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
return { status: 'ok' };
@@ -530,7 +581,7 @@ module.exports = {
try {
existing.subprocess.kill();
} catch (err) {
logger.error(extractErrorLogData(err), 'Error killing subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00117 Error killing subprocess');
}
}
this.opened = this.opened.filter(x => x.conid != conid || x.database != database);
@@ -553,7 +604,7 @@ module.exports = {
disconnect_meta: true,
async disconnect({ conid, database }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
await this.close(conid, database, true);
return { status: 'ok' };
},
@@ -563,8 +614,9 @@ module.exports = {
if (!conid || !database) {
return {};
}
const loadedPermissions = await loadPermissionsFromRequest(req);
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req, loadedPermissions);
if (conid == '__model') {
const model = await importDbModel(database);
const trans = await loadModelTransform(modelTransFile);
@@ -586,6 +638,46 @@ module.exports = {
message: `Loaded database structure for ${database}`,
});
if (process.env.STORAGE_DATABASE && !hasPermission(`all-tables`, loadedPermissions)) {
// filter databases by permissions
const tablePermissions = await loadTablePermissionsFromRequest(req);
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const databasePermissionRole = getDatabasePermissionRole(conid, database, databasePermissions);
function applyTablePermissionRole(list, objectTypeField) {
const res = [];
for (const item of list ?? []) {
const tablePermissionRole = getTablePermissionRole(
conid,
database,
objectTypeField,
item.schemaName,
item.pureName,
tablePermissions,
databasePermissionRole
);
if (tablePermissionRole != 'deny') {
res.push({
...item,
tablePermissionRole,
});
}
}
return res;
}
const res = {
...opened.structure,
tables: applyTablePermissionRole(opened.structure.tables, 'tables'),
views: applyTablePermissionRole(opened.structure.views, 'views'),
procedures: applyTablePermissionRole(opened.structure.procedures, 'procedures'),
functions: applyTablePermissionRole(opened.structure.functions, 'functions'),
triggers: applyTablePermissionRole(opened.structure.triggers, 'triggers'),
collections: applyTablePermissionRole(opened.structure.collections, 'collections'),
};
return res;
}
return opened.structure;
// const existing = this.opened.find((x) => x.conid == conid && x.database == database);
// if (existing) return existing.status;
@@ -600,7 +692,7 @@ module.exports = {
if (!conid) {
return null;
}
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
if (!conid) return null;
const opened = await this.ensureOpened(conid, database);
return opened.serverVersion || null;
@@ -608,7 +700,7 @@ module.exports = {
sqlPreview_meta: true,
async sqlPreview({ conid, database, objects, options }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
// wait for structure
await this.structure({ conid, database });
@@ -619,7 +711,7 @@ module.exports = {
exportModel_meta: true,
async exportModel({ conid, database, outputFolder, schema }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const realFolder = outputFolder.startsWith('archive:')
? resolveArchiveFolder(outputFolder.substring('archive:'.length))
@@ -637,7 +729,7 @@ module.exports = {
exportModelSql_meta: true,
async exportModelSql({ conid, database, outputFolder, outputFile, schema }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const connection = await connections.getCore({ conid });
const driver = requireEngineDriver(connection);
@@ -651,7 +743,7 @@ module.exports = {
generateDeploySql_meta: true,
async generateDeploySql({ conid, database, archiveFolder }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, {
msgtype: 'generateDeploySql',
@@ -923,9 +1015,12 @@ module.exports = {
executeSessionQuery_meta: true,
async executeSessionQuery({ sesid, conid, database, sql }, req) {
testConnectionPermission(conid, req);
logger.info({ sesid, sql }, 'Processing query');
sessions.dispatchMessage(sesid, 'Query execution started');
await testConnectionPermission(conid, req);
logger.info({ sesid, sql }, 'DBGM-00010 Processing query');
sessions.dispatchMessage(sesid, {
message: 'Query execution started',
sql,
});
const opened = await this.ensureOpened(conid, database);
opened.subprocess.send({ msgtype: 'executeSessionQuery', sql, sesid });
@@ -935,7 +1030,7 @@ module.exports = {
evalJsonScript_meta: true,
async evalJsonScript({ conid, database, script, runid }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
opened.subprocess.send({ msgtype: 'evalJsonScript', script, runid });
+54 -19
View File
@@ -1,9 +1,14 @@
const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir, jsldir } = require('../utility/directories');
const getChartExport = require('../utility/getChartExport');
const { hasPermission } = require('../utility/hasPermission');
const {
hasPermission,
loadPermissionsFromRequest,
loadFilePermissionsFromRequest,
getFilePermissionRole,
} = require('../utility/hasPermission');
const socket = require('../utility/socket');
const scheduler = require('./scheduler');
const getDiagramExport = require('../utility/getDiagramExport');
@@ -13,6 +18,7 @@ const dbgateApi = require('../shell');
const { getLogger } = require('dbgate-tools');
const platformInfo = require('../utility/platformInfo');
const { checkSecureFilePathsWithoutDirectory, checkSecureDirectories } = require('../utility/security');
const { copyAppLogsIntoFile, getRecentAppLogRecords } = require('../utility/appLogStore');
const logger = getLogger('files');
function serialize(format, data) {
@@ -30,7 +36,8 @@ function deserialize(format, text) {
module.exports = {
list_meta: true,
async list({ folder }, req) {
if (!hasPermission(`files/${folder}/read`, req)) return [];
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return [];
const dir = path.join(filesdir(), folder);
if (!(await fs.exists(dir))) return [];
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
@@ -39,10 +46,11 @@ module.exports = {
listAll_meta: true,
async listAll(_params, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
const folders = await fs.readdir(filesdir());
const res = [];
for (const folder of folders) {
if (!hasPermission(`files/${folder}/read`, req)) continue;
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) continue;
const dir = path.join(filesdir(), folder);
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
res.push(...files);
@@ -52,7 +60,8 @@ module.exports = {
delete_meta: true,
async delete({ folder, file }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
return false;
}
@@ -64,7 +73,8 @@ module.exports = {
rename_meta: true,
async rename({ folder, file, newFile }, req) {
if (!hasPermission(`files/${folder}/write`, req)) return false;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
return false;
}
@@ -85,10 +95,11 @@ module.exports = {
copy_meta: true,
async copy({ folder, file, newFile }, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
return false;
}
if (!hasPermission(`files/${folder}/write`, req)) return false;
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
@@ -112,7 +123,8 @@ module.exports = {
});
return deserialize(format, text);
} else {
if (!hasPermission(`files/${folder}/read`, req)) return null;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return null;
const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
return deserialize(format, text);
}
@@ -130,18 +142,19 @@ module.exports = {
save_meta: true,
async save({ folder, file, data, format }, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
return false;
}
if (folder.startsWith('archive:')) {
if (!hasPermission(`archive/write`, req)) return false;
if (!hasPermission(`archive/write`, loadedPermissions)) 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: folder.substring('archive:'.length) });
return true;
} else if (folder.startsWith('app:')) {
if (!hasPermission(`apps/write`, req)) return false;
if (!hasPermission(`apps/write`, loadedPermissions)) 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 });
@@ -149,7 +162,7 @@ module.exports = {
apps.emitChangedDbApp(folder);
return true;
} else {
if (!hasPermission(`files/${folder}/write`, req)) return false;
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
const dir = path.join(filesdir(), folder);
if (!(await fs.exists(dir))) {
await fs.mkdir(dir);
@@ -176,7 +189,8 @@ module.exports = {
favorites_meta: true,
async favorites(_params, req) {
if (!hasPermission(`files/favorites/read`, req)) return [];
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/favorites/read`, loadedPermissions)) return [];
const dir = path.join(filesdir(), 'favorites');
if (!(await fs.exists(dir))) return [];
const files = await fs.readdir(dir);
@@ -233,16 +247,17 @@ module.exports = {
getFileRealPath_meta: true,
async getFileRealPath({ folder, file }, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (folder.startsWith('archive:')) {
if (!hasPermission(`archive/write`, req)) return false;
if (!hasPermission(`archive/write`, loadedPermissions)) return false;
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
return path.join(dir, file);
} else if (folder.startsWith('app:')) {
if (!hasPermission(`apps/write`, req)) return false;
if (!hasPermission(`apps/write`, loadedPermissions)) return false;
const app = folder.substring('app:'.length);
return path.join(appdir(), app, file);
} else {
if (!hasPermission(`files/${folder}/write`, req)) return false;
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
const dir = path.join(filesdir(), folder);
if (!(await fs.exists(dir))) {
await fs.mkdir(dir);
@@ -253,7 +268,7 @@ module.exports = {
createZipFromJsons_meta: true,
async createZipFromJsons({ db, filePath }) {
logger.info(`Creating zip file from JSONS ${filePath}`);
logger.info(`DBGM-00011 Creating zip file from JSONS ${filePath}`);
await dbgateApi.zipJsonLinesData(db, filePath);
return true;
},
@@ -279,7 +294,7 @@ module.exports = {
const FOLDERS = ['sql', 'sqlite'];
for (const folder of FOLDERS) {
if (fileName.toLowerCase().endsWith('.' + folder)) {
logger.info(`Saving ${folder} file ${fileName}`);
logger.info(`DBGM-00012 Saving ${folder} file ${fileName}`);
await fs.copyFile(filePath, path.join(filesdir(), folder, fileName));
socket.emitChanged(`files-changed`, { folder: folder });
@@ -291,12 +306,13 @@ module.exports = {
}
}
throw new Error(`${fileName} doesn't have one of supported extensions: ${FOLDERS.join(', ')}`);
throw new Error(`DBGM-00013 ${fileName} doesn't have one of supported extensions: ${FOLDERS.join(', ')}`);
},
exportFile_meta: true,
async exportFile({ folder, file, filePath }, req) {
if (!hasPermission(`files/${folder}/read`, req)) return false;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return false;
await fs.copyFile(path.join(filesdir(), folder, file), filePath);
return true;
},
@@ -311,4 +327,23 @@ module.exports = {
await fs.copyFile(sourceFilePath, targetFilePath);
return true;
},
fillAppLogs_meta: true,
async fillAppLogs({ dateFrom = 0, dateTo = new Date().getTime(), prepareForExport = false }) {
const jslid = crypto.randomUUID();
const outputFile = path.join(jsldir(), `${jslid}.jsonl`);
await copyAppLogsIntoFile(dateFrom, dateTo, outputFile, prepareForExport);
return {
jslid,
};
},
getRecentAppLog_meta: true,
getRecentAppLog({ limit }) {
const res = getRecentAppLogRecords();
if (limit) {
return res.slice(-limit);
}
return res;
},
};
+7 -4
View File
@@ -7,7 +7,7 @@ const socket = require('../utility/socket');
const compareVersions = require('compare-versions');
const requirePlugin = require('../shell/requirePlugin');
const downloadPackage = require('../utility/downloadPackage');
const { hasPermission } = require('../utility/hasPermission');
const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
const _ = require('lodash');
const packagedPluginsContent = require('../packagedPluginsContent');
@@ -118,7 +118,8 @@ module.exports = {
install_meta: true,
async install({ packageName }, req) {
if (!hasPermission(`plugins/install`, req)) return;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`plugins/install`, loadedPermissions)) return;
const dir = path.join(pluginsdir(), packageName);
// @ts-ignore
if (!(await fs.exists(dir))) {
@@ -132,7 +133,8 @@ module.exports = {
uninstall_meta: true,
async uninstall({ packageName }, req) {
if (!hasPermission(`plugins/install`, req)) return;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`plugins/install`, loadedPermissions)) return;
const dir = path.join(pluginsdir(), packageName);
await fs.rmdir(dir, { recursive: true });
socket.emitChanged(`installed-plugins-changed`);
@@ -143,7 +145,8 @@ module.exports = {
upgrade_meta: true,
async upgrade({ packageName }, req) {
if (!hasPermission(`plugins/install`, req)) return;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`plugins/install`, loadedPermissions)) return;
const dir = path.join(pluginsdir(), packageName);
// @ts-ignore
if (await fs.exists(dir)) {
+10 -6
View File
@@ -21,6 +21,7 @@ const processArgs = require('../utility/processArgs');
const platformInfo = require('../utility/platformInfo');
const { checkSecureDirectories, checkSecureDirectoriesInScript } = require('../utility/security');
const { sendToAuditLog, logJsonRunnerScript } = require('../utility/auditlog');
const { testStandardPermission } = require('../utility/hasPermission');
const logger = getLogger('runners');
function extractPlugins(script) {
@@ -48,7 +49,7 @@ require=null;
async function run() {
${script}
await dbgateApi.finalizer.run();
logger.info('Finished job script');
logger.info('DBGM-00014 Finished job script');
}
dbgateApi.runScript(run);
`;
@@ -74,7 +75,8 @@ module.exports = {
dispatchMessage(runid, message) {
if (message) {
if (_.isPlainObject(message)) logger.log(message);
if (_.isPlainObject(message))
logger.log({ ...message, msg: message.msg || message.message || '', message: undefined });
else logger.info(message);
const toEmit = _.isPlainObject(message)
@@ -132,7 +134,7 @@ module.exports = {
const pluginNames = extractPlugins(scriptText);
// console.log('********************** SCRIPT TEXT **********************');
// console.log(scriptText);
logger.info({ scriptFile }, 'Running script');
logger.info({ scriptFile }, 'DBGM-00015 Running script');
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
const subprocess = fork(
scriptFile,
@@ -171,7 +173,7 @@ module.exports = {
subprocess.on('exit', code => {
// console.log('... EXITED', code);
this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' });
logger.info({ code, pid: subprocess.pid }, 'Exited process');
logger.info({ code, pid: subprocess.pid }, 'DBGM-00016 Exited process');
socket.emit(`runner-done-${runid}`, code);
this.opened = this.opened.filter(x => x.runid != runid);
});
@@ -222,7 +224,7 @@ module.exports = {
subprocess.on('exit', code => {
console.log('... EXITED', code);
logger.info({ code, pid: subprocess.pid }, 'Exited process');
logger.info({ code, pid: subprocess.pid }, 'DBGM-00017 Exited process');
this.dispatchMessage(runid, `Finished external process with code ${code}`);
socket.emit(`runner-done-${runid}`, code);
if (onFinished) {
@@ -258,7 +260,7 @@ module.exports = {
severity: 'error',
message: extractErrorMessage(err),
});
logger.error(extractErrorLogData(err), 'Caught error on stdin');
logger.error(extractErrorLogData(err), 'DBGM-00118 Caught error on stdin');
});
}
@@ -287,6 +289,8 @@ module.exports = {
return this.startCore(runid, scriptTemplate(js, false));
}
await testStandardPermission('run-shell-script', req);
if (!platformInfo.allowShellScripting) {
sendToAuditLog(req, {
category: 'shell',
+4 -3
View File
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
const path = require('path');
const cron = require('node-cron');
const runners = require('./runners');
const { hasPermission } = require('../utility/hasPermission');
const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
const { getLogger } = require('dbgate-tools');
const logger = getLogger('scheduler');
@@ -24,13 +24,14 @@ module.exports = {
if (!match) return;
const pattern = match[1];
if (!cron.validate(pattern)) return;
logger.info(`Schedule script ${file} with pattern ${pattern}`);
logger.info(`DBGM-00018 Schedule script ${file} with pattern ${pattern}`);
const task = cron.schedule(pattern, () => runners.start({ script: text }));
this.tasks.push(task);
},
async reload(_params, req) {
if (!hasPermission('files/shell/read', req)) return;
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission('files/shell/read', loadedPermissions)) return;
const shellDir = path.join(filesdir(), 'shell');
await this.unload();
if (!(await fs.exists(shellDir))) return;
@@ -8,7 +8,13 @@ const { handleProcessCommunication } = require('../utility/processComm');
const lock = new AsyncLock();
const config = require('./config');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
const {
testConnectionPermission,
loadPermissionsFromRequest,
hasPermission,
loadDatabasePermissionsFromRequest,
getDatabasePermissionRole,
} = require('../utility/hasPermission');
const { MissingCredentialsError } = require('../utility/exceptions');
const pipeForkLogs = require('../utility/pipeForkLogs');
const { getLogger, extractErrorLogData } = require('dbgate-tools');
@@ -103,7 +109,7 @@ module.exports = {
this.close(conid, false);
});
subprocess.on('error', err => {
logger.error(extractErrorLogData(err), 'Error in server connection subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00119 Error in server connection subprocess');
if (newOpened.disconnected) return;
this.close(conid, false);
});
@@ -121,7 +127,7 @@ module.exports = {
try {
existing.subprocess.kill();
} catch (err) {
logger.error(extractErrorLogData(err), 'Error killing subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00120 Error killing subprocess');
}
}
this.opened = this.opened.filter(x => x.conid != conid);
@@ -135,7 +141,7 @@ module.exports = {
disconnect_meta: true,
async disconnect({ conid }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
await this.close(conid, true);
return { status: 'ok' };
},
@@ -144,7 +150,9 @@ module.exports = {
async listDatabases({ conid }, req) {
if (!conid) return [];
if (conid == '__model') return [];
testConnectionPermission(conid, req);
const loadedPermissions = await loadPermissionsFromRequest(req);
await testConnectionPermission(conid, req, loadedPermissions);
const opened = await this.ensureOpened(conid);
sendToAuditLog(req, {
category: 'serverop',
@@ -157,12 +165,29 @@ module.exports = {
sessionGroup: 'listDatabases',
message: `Loaded databases for connection`,
});
if (process.env.STORAGE_DATABASE && !hasPermission(`all-databases`, loadedPermissions)) {
// filter databases by permissions
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const res = [];
for (const db of opened?.databases ?? []) {
const databasePermissionRole = getDatabasePermissionRole(db.id, db.name, databasePermissions);
if (databasePermissionRole != 'deny') {
res.push({
...db,
databasePermissionRole,
});
}
}
return res;
}
return opened?.databases ?? [];
},
version_meta: true,
async version({ conid }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
return opened?.version ?? null;
},
@@ -184,14 +209,14 @@ module.exports = {
return Promise.resolve();
}
this.lastPinged[conid] = new Date().getTime();
const opened = await this.ensureOpened(conid);
if (!opened) {
return Promise.resolve();
}
try {
const opened = await this.ensureOpened(conid);
if (!opened) {
return Promise.resolve();
}
opened.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error pinging server connection');
logger.error(extractErrorLogData(err), 'DBGM-00121 Error pinging server connection');
this.close(conid);
}
})
@@ -202,7 +227,7 @@ module.exports = {
refresh_meta: true,
async refresh({ conid, keepOpen }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
if (!keepOpen) this.close(conid);
await this.ensureOpened(conid);
@@ -210,7 +235,7 @@ module.exports = {
},
async sendDatabaseOp({ conid, msgtype, name }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (!opened) {
return null;
@@ -244,7 +269,7 @@ module.exports = {
try {
conn.subprocess.send({ msgid, ...message });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error sending request');
logger.error(extractErrorLogData(err), 'DBGM-00122 Error sending request');
this.close(conn.conid);
}
});
@@ -252,7 +277,7 @@ module.exports = {
},
async loadDataCore(msgtype, { conid, ...args }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (!opened) {
return null;
@@ -270,13 +295,43 @@ module.exports = {
serverSummary_meta: true,
async serverSummary({ conid }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
logger.info({ conid }, 'DBGM-00260 Processing server summary');
return this.loadDataCore('serverSummary', { conid });
},
listDatabaseProcesses_meta: true,
async listDatabaseProcesses(ctx, req) {
const { conid } = ctx;
// logger.info({ conid }, 'DBGM-00261 Listing processes of database server');
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (!opened) {
return null;
}
if (opened.connection.isReadOnly) return false;
return this.sendRequest(opened, { msgtype: 'listDatabaseProcesses' });
},
killDatabaseProcess_meta: true,
async killDatabaseProcess(ctx, req) {
const { conid, pid } = ctx;
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (!opened) {
return null;
}
if (opened.connection.isReadOnly) return false;
return this.sendRequest(opened, { msgtype: 'killDatabaseProcess', pid });
},
summaryCommand_meta: true,
async summaryCommand({ conid, command, row }, req) {
testConnectionPermission(conid, req);
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (!opened) {
return null;
+41 -6
View File
@@ -8,10 +8,13 @@ const path = require('path');
const { handleProcessCommunication } = require('../utility/processComm');
const processArgs = require('../utility/processArgs');
const { appdir } = require('../utility/directories');
const { getLogger, extractErrorLogData } = require('dbgate-tools');
const { getLogger, extractErrorLogData, removeSqlFrontMatter } = require('dbgate-tools');
const pipeForkLogs = require('../utility/pipeForkLogs');
const config = require('./config');
const { sendToAuditLog } = require('../utility/auditlog');
const { testStandardPermission, testDatabaseRolePermission } = require('../utility/hasPermission');
const { getStaticTokenSecret } = require('../auth/authCommon');
const jwt = require('jsonwebtoken');
const logger = getLogger('sessions');
@@ -80,6 +83,16 @@ module.exports = {
socket.emit(`session-recordset-${sesid}`, { jslid, resultIndex });
},
handle_endrecordset(sesid, props) {
const { jslid, rowCount, durationMs } = props;
this.dispatchMessage(sesid, {
message: `Query returned ${rowCount} rows in ${durationMs} ms`,
rowCount,
durationMs,
jslid,
});
},
handle_stats(sesid, stats) {
jsldata.notifyChangedStats(stats);
},
@@ -94,6 +107,12 @@ module.exports = {
socket.emit(`session-initialize-file-${jslid}`);
},
handle_changedCurrentDatabase(sesid, props) {
const { database } = props;
this.dispatchMessage(sesid, `Current database changed to ${database}`);
socket.emit(`session-changedb-${sesid}`, { database });
},
handle_ping() {},
create_meta: true,
@@ -148,10 +167,23 @@ module.exports = {
executeQuery_meta: true,
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
let useTokenIsOk = false;
if (frontMatter?.useToken) {
const decoded = jwt.verify(frontMatter.useToken, getStaticTokenSecret());
if (decoded?.['contentHash'] == crypto.createHash('md5').update(removeSqlFrontMatter(sql)).digest('hex')) {
useTokenIsOk = true;
}
}
if (!useTokenIsOk) {
await testStandardPermission('dbops/query', req);
}
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
if (!useTokenIsOk) {
await testDatabaseRolePermission(session.conid, session.database, 'run_script', req);
}
sendToAuditLog(req, {
category: 'dbop',
@@ -165,8 +197,11 @@ module.exports = {
message: 'Executing query',
});
logger.info({ sesid, sql }, 'Processing query');
this.dispatchMessage(sesid, 'Query execution started');
logger.info({ sesid, sql }, 'DBGM-00019 Processing query');
this.dispatchMessage(sesid, {
message: 'Query execution started',
sql,
});
session.subprocess.send({
msgtype: 'executeQuery',
sql,
@@ -186,7 +221,7 @@ module.exports = {
throw new Error('Invalid session');
}
logger.info({ sesid, command }, 'Processing control command');
logger.info({ sesid, command }, 'DBGM-00020 Processing control command');
this.dispatchMessage(sesid, `${_.startCase(command)} started`);
session.subprocess.send({ msgtype: 'executeControlCommand', command });
@@ -224,7 +259,7 @@ module.exports = {
throw new Error('Invalid session');
}
logger.info({ sesid }, 'Starting profiler');
logger.info({ sesid }, 'DBGM-00021 Starting profiler');
session.loadingReader_jslid = jslid;
session.subprocess.send({ msgtype: 'startProfiler', jslid });
@@ -271,7 +306,7 @@ module.exports = {
try {
session.subprocess.send({ msgtype: 'ping' });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error pinging session');
logger.error(extractErrorLogData(err), 'DBGM-00145 Error pinging session');
return {
status: 'error',
-4
View File
@@ -13,10 +13,6 @@ module.exports = {
return null;
},
async loadSuperadminPermissions() {
return [];
},
getConnectionsForLoginPage_meta: true,
async getConnectionsForLoginPage() {
return null;
@@ -0,0 +1,6 @@
module.exports = {
list_meta: true,
async list(req) {
return [];
},
};
+67 -96
View File
@@ -1,19 +1,8 @@
const crypto = require('crypto');
const path = require('path');
const { uploadsdir, getLogsFilePath, filesdir } = require('../utility/directories');
const { getLogger, extractErrorLogData } = require('dbgate-tools');
const { uploadsdir } = 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');
const socket = require('../utility/socket');
module.exports = {
upload_meta: {
@@ -28,7 +17,7 @@ module.exports = {
}
const uploadName = crypto.randomUUID();
const filePath = path.join(uploadsdir(), uploadName);
logger.info(`Uploading file ${data.name}, size=${data.size}`);
logger.info(`DBGM-00025 Uploading file ${data.name}, size=${data.size}`);
data.mv(filePath, () => {
res.json({
@@ -51,88 +40,70 @@ module.exports = {
res.sendFile(path.join(uploadsdir(), req.query.file));
},
async getGistToken() {
const settings = await config.getSettings();
// 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 settings['other.gistCreateToken'] || gistSecret;
},
// return response.data;
// } catch (err) {
// logger.error(extractErrorLogData(err), 'DBGM-00148 Error uploading gist');
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(extractErrorLogData(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;
},
// return {
// apiErrorMessage: err.message,
// };
// // console.error('Error creating gist:', error.response ? error.response.data : error.message);
// }
// },
};
-1
View File
@@ -1 +0,0 @@
module.exports = process.env.GIST_UPLOAD_SECRET;
+36 -5
View File
@@ -5,11 +5,12 @@ const moment = require('moment');
const path = require('path');
const { logsdir, setLogsFilePath, getLogsFilePath } = require('./utility/directories');
const currentVersion = require('./currentVersion');
const _ = require('lodash');
const logger = getLogger('apiIndex');
process.on('uncaughtException', err => {
logger.fatal(extractErrorLogData(err), 'Uncaught exception, exiting process');
logger.fatal(extractErrorLogData(err), 'DBGM-00259 Uncaught exception, exiting process');
process.exit(1);
});
@@ -33,6 +34,9 @@ if (processArgs.processDisplayName) {
// }
function configureLogger() {
const { initializeRecentLogProvider, pushToRecentLogs } = require('./utility/appLogStore');
initializeRecentLogProvider();
const logsFilePath = path.join(logsdir(), `${moment().format('YYYY-MM-DD-HH-mm')}-${process.pid}.ndjson`);
setLogsFilePath(logsFilePath);
setLoggerName('main');
@@ -40,6 +44,8 @@ function configureLogger() {
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 streamsByDatePart = {};
const logConfig = {
base: { pid: process.pid },
targets: [
@@ -49,10 +55,35 @@ function configureLogger() {
level: consoleLogLevel,
},
{
type: 'stream',
type: 'objstream',
// @ts-ignore
level: fileLogLevel,
stream: fs.createWriteStream(logsFilePath, { flags: 'a' }),
objstream: {
send(msg) {
const datePart = moment(msg.time).format('YYYY-MM-DD');
if (!streamsByDatePart[datePart]) {
streamsByDatePart[datePart] = fs.createWriteStream(
path.join(logsdir(), `${moment().format('YYYY-MM-DD-HH-mm')}-${process.pid}.ndjson`),
{ flags: 'a' }
);
}
const additionals = {};
const finalMsg =
_.isString(msg.msg) && msg.msg.match(/^DBGM-\d\d\d\d\d/)
? {
...msg,
msg: msg.msg.substring(10).trimStart(),
msgcode: msg.msg.substring(0, 10),
...additionals,
}
: {
...msg,
...additionals,
};
streamsByDatePart[datePart].write(`${JSON.stringify(finalMsg)}\n`);
pushToRecentLogs(finalMsg);
},
},
},
],
};
@@ -101,10 +132,10 @@ function configureLogger() {
if (processArgs.listenApi) {
configureLogger();
logger.info(`Starting API process version ${currentVersion.version}`);
logger.info(`DBGM-00026 Starting API process version ${currentVersion.version}`);
if (process.env.DEBUG_PRINT_ENV_VARIABLES) {
logger.info('Debug print environment variables:');
logger.info('DBGM-00027 Debug print environment variables:');
for (const key of Object.keys(process.env)) {
logger.info(` ${key}: ${JSON.stringify(process.env[key])}`);
}
+62 -20
View File
@@ -6,6 +6,7 @@ const http = require('http');
const cors = require('cors');
const getPort = require('get-port');
const path = require('path');
const fs = require('fs/promises');
const useController = require('./utility/useController');
const socket = require('./utility/socket');
@@ -28,6 +29,8 @@ const files = require('./controllers/files');
const scheduler = require('./controllers/scheduler');
const queryHistory = require('./controllers/queryHistory');
const cloud = require('./controllers/cloud');
const teamFiles = require('./controllers/teamFiles');
const onFinished = require('on-finished');
const processArgs = require('./utility/processArgs');
@@ -44,6 +47,48 @@ const { startCloudFiles } = require('./utility/cloudIntf');
const logger = getLogger('main');
function registerExpressStatic(app, publicDir) {
app.get([getExpressPath('/'), getExpressPath('/*.html')], async (req, res, next) => {
try {
const relPath = req.path === getExpressPath('/') ? '/index.html' : req.path;
const filePath = path.join(publicDir, relPath);
let html = await fs.readFile(filePath, 'utf8');
if (process.env.DBGATE_GTM_ID) {
html = html.replace(
/<!--HEAD_SCRIPT-->/g,
`<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${process.env.DBGATE_GTM_ID}');</script>
<!-- End Google Tag Manager -->`
);
html = html.replace(
/<!--BODY_SCRIPT-->/g,
process.env.PAGE_BODY_SCRIPT ??
`<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${process.env.DBGATE_GTM_ID}" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->`
);
} else {
html = html.replace(/<!--HEAD_SCRIPT-->/g, process.env.PAGE_HEAD_SCRIPT ?? '');
html = html.replace(/<!--BODY_SCRIPT-->/g, process.env.PAGE_BODY_SCRIPT ?? '');
}
res.type('html').send(html);
} catch (err) {
if (err.code === 'ENOENT') return next();
next(err);
}
});
// 2) Static assets for everything else (css/js/images/etc.)
app.use(getExpressPath('/'), express.static(publicDir));
}
function start() {
// console.log('process.argv', process.argv);
@@ -78,22 +123,18 @@ function start() {
if (platformInfo.isDocker) {
// server static files inside docker container
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
registerExpressStatic(app, '/home/dbgate-docker/public');
} else if (platformInfo.isAwsUbuntuLayout) {
app.use(getExpressPath('/'), express.static('/home/ubuntu/build/public'));
registerExpressStatic(app, '/home/dbgate-docker/public');
registerExpressStatic(app, '/home/ubuntu/build/public');
} else if (platformInfo.isAzureUbuntuLayout) {
app.use(getExpressPath('/'), express.static('/home/azureuser/build/public'));
registerExpressStatic(app, '/home/azureuser/build/public');
} else if (processArgs.runE2eTests) {
app.use(getExpressPath('/'), express.static(path.resolve('packer/build/public')));
registerExpressStatic(app, path.resolve('packer/build/public'));
} else if (platformInfo.isNpmDist) {
app.use(
getExpressPath('/'),
express.static(path.join(__dirname, isProApp() ? '../../dbgate-web-premium/public' : '../../dbgate-web/public'))
);
registerExpressStatic(app, path.join(__dirname, isProApp() ? '../../dbgate-web-premium/public' : '../../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')));
registerExpressStatic(app, path.join(__dirname, '../../web/public'));
} else {
app.get(getExpressPath('/'), (req, res) => {
res.send('DbGate API');
@@ -152,15 +193,15 @@ function start() {
if (platformInfo.isDocker) {
const port = process.env.PORT || 3000;
logger.info(`DbGate API listening on port ${port} (docker build)`);
logger.info(`DBGM-00028 DbGate API listening on port ${port} (docker build)`);
server.listen(port);
} else if (platformInfo.isAwsUbuntuLayout) {
const port = process.env.PORT || 3000;
logger.info(`DbGate API listening on port ${port} (AWS AMI build)`);
logger.info(`DBGM-00029 DbGate API listening on port ${port} (AWS AMI build)`);
server.listen(port);
} else if (platformInfo.isAzureUbuntuLayout) {
const port = process.env.PORT || 3000;
logger.info(`DbGate API listening on port ${port} (Azure VM build)`);
logger.info(`DBGM-00030 DbGate API listening on port ${port} (Azure VM build)`);
server.listen(port);
} else if (platformInfo.isNpmDist) {
getPort({
@@ -170,27 +211,27 @@ function start() {
),
}).then(port => {
server.listen(port, () => {
logger.info(`DbGate API listening on port ${port} (NPM build)`);
logger.info(`DBGM-00031 DbGate API listening on port ${port} (NPM build)`);
});
});
} else if (process.env.DEVWEB) {
const port = process.env.PORT || 3000;
logger.info(`DbGate API & web listening on port ${port} (dev web build)`);
logger.info(`DBGM-00032 DbGate API & web listening on port ${port} (dev web build)`);
server.listen(port);
} else {
const port = process.env.PORT || 3000;
logger.info(`DbGate API listening on port ${port} (dev API build)`);
logger.info(`DBGM-00033 DbGate API listening on port ${port} (dev API build)`);
server.listen(port);
}
function shutdown() {
logger.info('\nShutting down DbGate API server');
logger.info('DBGM-00034 Shutting down DbGate API server');
server.close(() => {
logger.info('Server shut down, terminating');
logger.info('DBGM-00035 Server shut down, terminating');
process.exit(0);
});
setTimeout(() => {
logger.info('Server close timeout, terminating');
logger.info('DBGM-00036 Server close timeout, terminating');
process.exit(0);
}, 1000);
}
@@ -225,6 +266,7 @@ function useAllControllers(app, electron) {
useController(app, electron, '/apps', apps);
useController(app, electron, '/auth', auth);
useController(app, electron, '/cloud', cloud);
useController(app, electron, '/team-files', teamFiles);
}
function setElectronSender(electronSender) {
@@ -6,7 +6,6 @@ const {
extractIntSettingsValue,
getLogger,
isCompositeDbName,
dbNameLogCategory,
extractErrorMessage,
extractErrorLogData,
ScriptWriterEval,
@@ -18,13 +17,14 @@ const requireEngineDriver = require('../utility/requireEngineDriver');
const { connectUtility } = require('../utility/connectUtility');
const { handleProcessCommunication } = require('../utility/processComm');
const generateDeploySql = require('../shell/generateDeploySql');
const { dumpSqlSelect } = require('dbgate-sqltree');
const { dumpSqlSelect, scriptToSql } = require('dbgate-sqltree');
const { allowExecuteCustomScript, handleQueryStream } = require('../utility/handleQueryStream');
const dbgateApi = require('../shell');
const requirePlugin = require('../shell/requirePlugin');
const path = require('path');
const { rundir } = require('../utility/directories');
const fs = require('fs-extra');
const { changeSetToSql } = require('dbgate-datalib');
const logger = getLogger('dbconnProcess');
@@ -45,6 +45,14 @@ function getStatusCounter() {
return statusCounter;
}
function getLogInfo() {
return {
database: dbhan ? dbhan.database : undefined,
conid: dbhan ? dbhan.conid : undefined,
engine: storedConnection ? storedConnection.engine : undefined,
};
}
async function checkedAsyncCall(promise) {
try {
const res = await promise;
@@ -131,10 +139,10 @@ async function readVersion() {
const driver = requireEngineDriver(storedConnection);
try {
const version = await driver.getVersion(dbhan);
logger.debug(`Got server version: ${version.version}`);
logger.debug(getLogInfo(), `DBGM-00037 Got server version: ${version.version}`);
serverVersion = version;
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting DB server version');
logger.error(extractErrorLogData(err, getLogInfo()), 'DBGM-00149 Error getting DB server version');
serverVersion = { version: 'Unknown' };
}
process.send({ msgtype: 'version', version: serverVersion });
@@ -148,9 +156,8 @@ async function handleConnect({ connection, structure, globalSettings }) {
const driver = requireEngineDriver(storedConnection);
dbhan = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
logger.debug(
`Connected to database, driver: ${storedConnection.engine}, separate schemas: ${
storedConnection.useSeparateSchemas ? 'YES' : 'NO'
}, 'DB: ${dbNameLogCategory(dbhan.database)} }`
getLogInfo(),
`DBGM-00038 Connected to database, separate schemas: ${storedConnection.useSeparateSchemas ? 'YES' : 'NO'}`
);
dbhan.feedback = feedback => setStatus({ feedback });
await checkedAsyncCall(readVersion());
@@ -257,13 +264,16 @@ async function handleDriverDataCore(msgid, callMethod, { logName }) {
const result = await callMethod(driver);
process.send({ msgtype: 'response', msgid, result: serializeJsTypesForJsonStringify(result) });
} catch (err) {
logger.error(extractErrorLogData(err, { logName }), `Error when handling message ${logName}`);
logger.error(
extractErrorLogData(err, { logName, ...getLogInfo() }),
`DBGM-00150 Error when handling message ${logName}`
);
process.send({ msgtype: 'response', msgid, errorMessage: extractErrorMessage(err, 'Error executing DB data') });
}
}
async function handleSchemaList({ msgid }) {
logger.debug('Loading schema list');
logger.debug(getLogInfo(), 'DBGM-00039 Loading schema list');
return handleDriverDataCore(msgid, driver => driver.listSchemas(dbhan), { logName: 'listSchemas' });
}
@@ -339,6 +349,25 @@ async function handleUpdateCollection({ msgid, changeSet }) {
}
}
async function handleSaveTableData({ msgid, changeSet }) {
await waitStructure();
try {
const driver = requireEngineDriver(storedConnection);
const script = driver.createSaveChangeSetScript(changeSet, analysedStructure, () =>
changeSetToSql(changeSet, analysedStructure, driver.dialect)
);
const sql = scriptToSql(driver, script);
await driver.script(dbhan, sql, { useTransaction: true });
process.send({ msgtype: 'response', msgid });
} catch (err) {
process.send({
msgtype: 'response',
msgid,
errorMessage: extractErrorMessage(err, 'Error executing SQL script'),
});
}
}
async function handleSqlPreview({ msgid, objects, options }) {
await waitStructure();
const driver = requireEngineDriver(storedConnection);
@@ -351,7 +380,7 @@ async function handleSqlPreview({ msgid, objects, options }) {
process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
if (generator.isUnhandledException) {
setTimeout(async () => {
logger.error('Exiting because of unhandled exception');
logger.error(getLogInfo(), 'DBGM-00151 Exiting because of unhandled exception');
await driver.close(dbhan);
process.exit(0);
}, 500);
@@ -455,6 +484,7 @@ const messageHandlers = {
runScript: handleRunScript,
runOperation: handleRunOperation,
updateCollection: handleUpdateCollection,
saveTableData: handleSaveTableData,
collectionData: handleCollectionData,
loadKeys: handleLoadKeys,
scanKeys: handleScanKeys,
@@ -485,7 +515,7 @@ function start() {
setInterval(async () => {
const time = new Date().getTime();
if (time - lastPing > 40 * 1000) {
logger.info('Database connection not alive, exiting');
logger.info(getLogInfo(), 'DBGM-00040 Database connection not alive, exiting');
const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan);
process.exit(0);
@@ -497,10 +527,10 @@ function start() {
try {
await handleMessage(message);
} catch (err) {
logger.error(extractErrorLogData(err), 'Error in DB connection');
logger.error(extractErrorLogData(err, getLogInfo()), 'DBGM-00041 Error in DB connection');
process.send({
msgtype: 'error',
error: extractErrorMessage(err, 'Error processing message'),
error: extractErrorMessage(err, 'DBGM-00042 Error processing message'),
msgid: message?.msgid,
});
}
@@ -39,7 +39,7 @@ async function handleRefresh() {
name: 'error',
message: err.message,
});
logger.error(extractErrorLogData(err), 'Error refreshing server databases');
logger.error(extractErrorLogData(err), 'DBGM-00152 Error refreshing server databases');
setTimeout(() => process.exit(1), 1000);
}
}
@@ -50,7 +50,7 @@ async function readVersion() {
try {
version = await driver.getVersion(dbhan);
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting DB server version');
logger.error(extractErrorLogData(err), 'DBGM-00153 Error getting DB server version');
version = { version: 'Unknown' };
}
process.send({ msgtype: 'version', version });
@@ -90,7 +90,7 @@ async function handleConnect(connection) {
name: 'error',
message: err.message,
});
logger.error(extractErrorLogData(err), 'Error connecting to server');
logger.error(extractErrorLogData(err), 'DBGM-00154 Error connecting to server');
setTimeout(() => process.exit(1), 1000);
}
@@ -120,7 +120,7 @@ async function handleDatabaseOp(op, { msgid, name }) {
} else {
const dmp = driver.createDumper();
dmp[op](name);
logger.info({ sql: dmp.s }, 'Running script');
logger.info({ sql: dmp.s }, 'DBGM-00043 Running script');
await driver.query(dbhan, dmp.s, { discardResult: true });
}
await handleRefresh();
@@ -146,6 +146,30 @@ async function handleServerSummary({ msgid }) {
return handleDriverDataCore(msgid, driver => driver.serverSummary(dbhan));
}
async function handleKillDatabaseProcess({ msgid, pid }) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
const result = await driver.killProcess(dbhan, Number(pid));
process.send({ msgtype: 'response', msgid, result });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleListDatabaseProcesses({ msgid }) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
const result = await driver.listProcesses(dbhan);
process.send({ msgtype: 'response', msgid, result });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleSummaryCommand({ msgid, command, row }) {
return handleDriverDataCore(msgid, driver => driver.summaryCommand(dbhan, command, row));
}
@@ -154,6 +178,8 @@ const messageHandlers = {
connect: handleConnect,
ping: handlePing,
serverSummary: handleServerSummary,
killDatabaseProcess: handleKillDatabaseProcess,
listDatabaseProcesses: handleListDatabaseProcesses,
summaryCommand: handleSummaryCommand,
createDatabase: props => handleDatabaseOp('createDatabase', props),
dropDatabase: props => handleDatabaseOp('dropDatabase', props),
@@ -170,7 +196,7 @@ function start() {
setInterval(async () => {
const time = new Date().getTime();
if (time - lastPing > 40 * 1000) {
logger.info('Server connection not alive, exiting');
logger.info('DBGM-00044 Server connection not alive, exiting');
const driver = requireEngineDriver(storedConnection);
if (dbhan) {
await driver.close(dbhan);
@@ -188,7 +214,7 @@ function start() {
name: 'error',
message: err.message,
});
logger.error(extractErrorLogData(err), `Error processing message ${message?.['msgtype']}`);
logger.error(extractErrorLogData(err), `DBGM-00155 Error processing message ${message?.['msgtype']}`);
}
});
}
+2 -2
View File
@@ -230,7 +230,7 @@ function start() {
setInterval(async () => {
const time = new Date().getTime();
if (time - lastPing > 25 * 1000) {
logger.info('Session not alive, exiting');
logger.info('DBGM-00045 Session not alive, exiting');
const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan);
process.exit(0);
@@ -250,7 +250,7 @@ function start() {
!currentProfiler &&
executingScripts == 0
) {
logger.info('Session not active, exiting');
logger.info('DBGM-00046 Session not active, exiting');
const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan);
process.exit(0);
+1 -1
View File
@@ -41,7 +41,7 @@ async function handleStart({ connection, tunnelConfig }) {
tunnelConfig,
});
} catch (err) {
logger.error(extractErrorLogData(err), 'Error creating SSH tunnel connection:');
logger.error(extractErrorLogData(err), 'DBGM-00156 Error creating SSH tunnel connection:');
process.send({
msgtype: 'error',
+1 -1
View File
@@ -10,7 +10,7 @@ const logger = getLogger();
function archiveWriter({ folderName, fileName }) {
const dir = resolveArchiveFolder(folderName);
if (!fs.existsSync(dir)) {
logger.info(`Creating directory ${dir}`);
logger.info(`DBGM-00047 Creating directory ${dir}`);
fs.mkdirSync(dir);
}
const jsonlFile = path.join(dir, `${fileName}.jsonl`);
+1 -1
View File
@@ -83,7 +83,7 @@ async function copyStream(input, output, options) {
});
}
logger.error(extractErrorLogData(err, { progressName }), 'Import/export job failed');
logger.error(extractErrorLogData(err, { progressName }), 'DBGM-00157 Import/export job failed');
// throw err;
}
}
+3 -3
View File
@@ -28,20 +28,20 @@ async function executeQuery({
useTransaction,
}) {
if (!logScriptItems && !skipLogging) {
logger.info({ sql: getLimitedQuery(sql) }, `Execute query`);
logger.info({ sql: getLimitedQuery(sql) }, `DBGM-00048 Execute query`);
}
if (!driver) driver = requireEngineDriver(connection);
const dbhan = systemConnection || (await connectUtility(driver, connection, 'script'));
if (sqlFile) {
logger.debug(`Loading SQL file ${sqlFile}`);
logger.debug(`DBGM-00049 Loading SQL file ${sqlFile}`);
sql = await fs.readFile(sqlFile, { encoding: 'utf-8' });
}
try {
if (!skipLogging) {
logger.debug(`Running SQL query, length: ${sql.length}`);
logger.debug(`DBGM-00050 Running SQL query, length: ${sql.length}`);
}
await driver.script(dbhan, sql, { logScriptItems, useTransaction });
+3 -3
View File
@@ -45,14 +45,14 @@ class ImportStream extends stream.Transform {
}
async function importDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, inputFile }) {
logger.info(`Importing database`);
logger.info(`DBGM-00051 Importing database`);
if (!driver) driver = requireEngineDriver(connection);
const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
try {
logger.info(`Input file: ${inputFile}`);
logger.info(`DBGM-00052 Input file: ${inputFile}`);
const downloadedFile = await download(inputFile);
logger.info(`Downloaded file: ${downloadedFile}`);
logger.info(`DBGM-00053 Downloaded file: ${downloadedFile}`);
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
const splittedStream = splitQueryStream(fileStream, {
+1 -1
View File
@@ -42,7 +42,7 @@ class ParseStream extends stream.Transform {
* @returns {Promise<readerType>} - reader object
*/
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
logger.info(`Reading file ${fileName}`);
logger.info(`DBGM-00054 Reading file ${fileName}`);
const downloadedFile = await download(fileName);
+1 -1
View File
@@ -33,7 +33,7 @@ class StringifyStream extends stream.Transform {
* @returns {Promise<writerType>} - writer object
*/
async function jsonLinesWriter({ fileName, encoding = 'utf-8', header = true }) {
logger.info(`Writing file ${fileName}`);
logger.info(`DBGM-00055 Writing file ${fileName}`);
const stringify = new StringifyStream({ header });
const fileStream = fs.createWriteStream(fileName, encoding);
return [stringify, fileStream];
+1 -1
View File
@@ -63,7 +63,7 @@ async function jsonReader({
encoding = 'utf-8',
limitRows = undefined,
}) {
logger.info(`Reading file ${fileName}`);
logger.info(`DBGM-00056 Reading file ${fileName}`);
const downloadedFile = await download(fileName);
const fileStream = fs.createReadStream(
+1 -1
View File
@@ -96,7 +96,7 @@ class StringifyStream extends stream.Transform {
* @returns {Promise<writerType>} - writer object
*/
async function jsonWriter({ fileName, jsonStyle, keyField = '_key', rootField, encoding = 'utf-8' }) {
logger.info(`Writing file ${fileName}`);
logger.info(`DBGM-00057 Writing file ${fileName}`);
const stringify = new StringifyStream({ jsonStyle, keyField, rootField });
const fileStream = fs.createWriteStream(fileName, encoding);
return [stringify, fileStream];
+2 -2
View File
@@ -6,13 +6,13 @@ const exportDbModel = require('../utility/exportDbModel');
const logger = getLogger('analyseDb');
async function loadDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, outputDir }) {
logger.debug(`Analysing database`);
logger.debug(`DBGM-00058 Analysing database`);
if (!driver) driver = requireEngineDriver(connection);
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
try {
const dbInfo = await driver.analyseFull(dbhan);
logger.debug(`Analyse finished`);
logger.debug(`DBGM-00059 Analyse finished`);
await exportDbModel(dbInfo, outputDir);
} finally {
@@ -132,7 +132,7 @@ async function modifyJsonLinesReader({
mergeKey = null,
mergeMode = 'merge',
}) {
logger.info(`Reading file ${fileName} with change set`);
logger.info(`DBGM-00060 Reading file ${fileName} with change set`);
const fileStream = fs.createReadStream(
fileName,
+1 -1
View File
@@ -29,7 +29,7 @@ async function queryReader({
// if (!sql && !json) {
// throw new Error('One of sql or json must be set');
// }
logger.info({ sql: query || sql }, `Reading query`);
logger.info({ sql: query || sql }, `DBGM-00061 Reading query`);
// else console.log(`Reading query ${JSON.stringify(json)}`);
if (!driver) {
+6 -3
View File
@@ -4,7 +4,7 @@ const { pluginsdir, packagedPluginsDir, getPluginBackendPath } = require('../uti
const platformInfo = require('../utility/platformInfo');
const authProxy = require('../utility/authProxy');
const { getLogger } = require('dbgate-tools');
const { isProApp } = require('../utility/checkLicense');
//
const logger = getLogger('requirePlugin');
const loadedPlugins = {};
@@ -13,7 +13,10 @@ const dbgateEnv = {
dbgateApi: null,
platformInfo,
authProxy,
isProApp: isProApp()
isProApp: () =>{
const { isProApp } = require('../utility/checkLicense');
return isProApp();
}
};
function requirePlugin(packageName, requiredPlugin = null) {
if (!packageName) throw new Error('Missing packageName in plugin');
@@ -22,7 +25,7 @@ function requirePlugin(packageName, requiredPlugin = null) {
if (requiredPlugin == null) {
let module;
const modulePath = getPluginBackendPath(packageName);
logger.info(`Loading module ${packageName} from ${modulePath}`);
logger.info(`DBGM-00062 Loading module ${packageName} from ${modulePath}`);
try {
// @ts-ignore
module = __non_webpack_require__(modulePath);
+1 -1
View File
@@ -11,7 +11,7 @@ async function runScript(func) {
await func();
process.exit(0);
} catch (err) {
logger.error(extractErrorLogData(err), `Error running script`);
logger.error(extractErrorLogData(err), `DBGM-00158 Error running script`);
process.exit(1);
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ class SqlizeStream extends stream.Transform {
}
async function sqlDataWriter({ fileName, dataName, driver, encoding = 'utf-8' }) {
logger.info(`Writing file ${fileName}`);
logger.info(`DBGM-00063 Writing file ${fileName}`);
const stringify = new SqlizeStream({ fileName, dataName });
const fileStream = fs.createWriteStream(fileName, encoding);
return [stringify, fileStream];
+3 -3
View File
@@ -23,7 +23,7 @@ async function tableReader({ connection, systemConnection, pureName, schemaName,
if (driver.databaseEngineTypes.includes('document')) {
// @ts-ignore
logger.info(`Reading collection ${fullNameToString(fullName)}`);
logger.info(`DBGM-00064 Reading collection ${fullNameToString(fullName)}`);
// @ts-ignore
return await driver.readQuery(dbhan, JSON.stringify(fullName));
}
@@ -32,14 +32,14 @@ async function tableReader({ connection, systemConnection, pureName, schemaName,
const query = `select * from ${quoteFullName(driver.dialect, fullName)}`;
if (table) {
// @ts-ignore
logger.info(`Reading table ${fullNameToString(table)}`);
logger.info(`DBGM-00065 Reading table ${fullNameToString(table)}`);
// @ts-ignore
return await driver.readQuery(dbhan, query, table);
}
const view = await driver.analyseSingleObject(dbhan, fullName, 'views');
if (view) {
// @ts-ignore
logger.info(`Reading view ${fullNameToString(view)}`);
logger.info(`DBGM-00066 Reading view ${fullNameToString(view)}`);
// @ts-ignore
return await driver.readQuery(dbhan, query, view);
}
+1 -1
View File
@@ -20,7 +20,7 @@ const logger = getLogger('tableWriter');
* @returns {Promise<writerType>} - writer object
*/
async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
logger.info(`Writing table ${fullNameToString({ schemaName, pureName })}`);
logger.info(`DBGM-00067 Writing table ${fullNameToString({ schemaName, pureName })}`);
if (!driver) {
driver = requireEngineDriver(connection);
+4 -4
View File
@@ -52,14 +52,14 @@ function unzipDirectory(zipPath, outputDirectory) {
readStream.on('end', () => zipFile.readEntry());
writeStream.on('finish', () => {
logger.info(`Extracted "${entry.fileName}" → "${destPath}".`);
logger.info(`DBGM-00068 Extracted "${entry.fileName}" → "${destPath}".`);
res();
});
writeStream.on('error', writeErr => {
logger.error(
extractErrorLogData(writeErr),
`Error extracting "${entry.fileName}" from "${zipPath}".`
`DBGM-00069 Error extracting "${entry.fileName}" from "${zipPath}".`
);
rej(writeErr);
});
@@ -74,14 +74,14 @@ function unzipDirectory(zipPath, outputDirectory) {
zipFile.on('end', () => {
Promise.all(pending)
.then(() => {
logger.info(`Archive "${zipPath}" fully extracted to "${outputDirectory}".`);
logger.info(`DBGM-00070 Archive "${zipPath}" fully extracted to "${outputDirectory}".`);
resolve(true);
})
.catch(reject);
});
zipFile.on('error', err => {
logger.error(extractErrorLogData(err), `ZIP file error in ${zipPath}.`);
logger.error(extractErrorLogData(err), `DBGM-00071 ZIP file error in ${zipPath}.`);
reject(err);
});
});
+3 -3
View File
@@ -16,16 +16,16 @@ function zipDirectory(inputDirectory, outputFile) {
// Listen for all archive data to be written
output.on('close', () => {
logger.info(`ZIP file created (${archive.pointer()} total bytes)`);
logger.info(`DBGM-00072 ZIP file created (${archive.pointer()} total bytes)`);
resolve();
});
archive.on('warning', err => {
logger.warn(extractErrorLogData(err), `Warning while creating ZIP: ${err.message}`);
logger.warn(extractErrorLogData(err), `DBGM-00073 Warning while creating ZIP: ${err.message}`);
});
archive.on('error', err => {
logger.error(extractErrorLogData(err), `Error while creating ZIP: ${err.message}`);
logger.error(extractErrorLogData(err), `DBGM-00074 Error while creating ZIP: ${err.message}`);
reject(err);
});
+3 -3
View File
@@ -17,16 +17,16 @@ function zipDirectory(jsonDb, outputFile) {
// Listen for all archive data to be written
output.on('close', () => {
logger.info(`ZIP file created (${archive.pointer()} total bytes)`);
logger.info(`DBGM-00075 ZIP file created (${archive.pointer()} total bytes)`);
resolve();
});
archive.on('warning', err => {
logger.warn(extractErrorLogData(err), `Warning while creating ZIP: ${err.message}`);
logger.warn(extractErrorLogData(err), `DBGM-00076 Warning while creating ZIP: ${err.message}`);
});
archive.on('error', err => {
logger.error(extractErrorLogData(err), `Error while creating ZIP: ${err.message}`);
logger.error(extractErrorLogData(err), `DBGM-00077 Error while creating ZIP: ${err.message}`);
reject(err);
});
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -61,7 +61,7 @@ class DatastoreProxy {
this.subprocess = null;
});
this.subprocess.on('error', err => {
logger.error(extractErrorLogData(err), 'Error in data store subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00167 Error in data store subprocess');
this.subprocess = null;
});
this.subprocess.send({ msgtype: 'open', file: this.file });
@@ -77,7 +77,7 @@ class DatastoreProxy {
try {
this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting rows');
logger.error(extractErrorLogData(err), 'DBGM-00168 Error getting rows');
this.subprocess = null;
}
});
@@ -91,7 +91,7 @@ class DatastoreProxy {
try {
this.subprocess.send({ msgtype: 'notify', msgid });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error notifying subprocess');
logger.error(extractErrorLogData(err), 'DBGM-00169 Error notifying subprocess');
this.subprocess = null;
}
});
@@ -7,7 +7,6 @@ const AsyncLock = require('async-lock');
const lock = new AsyncLock();
const stableStringify = require('json-stable-stringify');
const { evaluateCondition } = require('dbgate-sqltree');
const requirePluginFunction = require('./requirePluginFunction');
const esort = require('external-sorting');
const { jsldir } = require('./directories');
const LineReader = require('./LineReader');
@@ -23,7 +22,10 @@ class JsonLinesDatastore {
this.notifyChangedCallback = null;
this.currentFilter = null;
this.currentSort = null;
this.rowFormatter = requirePluginFunction(formatterFunction);
if (formatterFunction) {
const requirePluginFunction = require('./requirePluginFunction');
this.rowFormatter = requirePluginFunction(formatterFunction);
}
this.sortedFiles = {};
}
+119
View File
@@ -0,0 +1,119 @@
const fs = require('fs-extra');
const path = require('path');
const { logsdir } = require('./directories');
const { format, addDays, startOfDay } = require('date-fns');
const LineReader = require('./LineReader');
const socket = require('./socket');
const _ = require('lodash');
async function getLogFiles(timeFrom, timeTo) {
const dir = logsdir();
const files = await fs.readdir(dir);
const startPrefix = format(timeFrom, 'yyyy-MM-dd');
const endPrefix = format(addDays(timeTo, 1), 'yyyy-MM-dd');
const logFiles = files
.filter(file => file.endsWith('.ndjson'))
.filter(file => file >= startPrefix && file < endPrefix);
return logFiles.sort().map(x => path.join(dir, x));
}
const RECENT_LOG_LIMIT = 1000;
let recentLogs = null;
const beforeRecentLogs = [];
function adjustRecentLogs() {
if (recentLogs.length > RECENT_LOG_LIMIT) {
recentLogs.splice(0, recentLogs.length - RECENT_LOG_LIMIT);
}
}
function prepareEntryForExport(entry, lastEntry) {
return {
date: format(new Date(entry.time), 'yyyy-MM-dd'),
time: format(new Date(entry.time), 'HH:mm:ss'),
dtime: lastEntry ? entry.time - lastEntry.time : 0,
msgcode: entry.msgcode || '',
message: entry.msg || '',
..._.omit(entry, ['time', 'msg', 'msgcode']),
conid: entry.conid || '',
database: entry.database || '',
engine: entry.engine || '',
ts: entry.time,
};
}
async function copyAppLogsIntoFile(timeFrom, timeTo, fileName, prepareForExport) {
const writeStream = fs.createWriteStream(fileName);
let lastEntry = null;
for (const file of await getLogFiles(timeFrom, timeTo)) {
const readStream = fs.createReadStream(file);
const reader = new LineReader(readStream);
do {
const line = await reader.readLine();
if (line == null) break;
try {
const logEntry = JSON.parse(line);
if (logEntry.time >= timeFrom && logEntry.time <= timeTo) {
writeStream.write(
JSON.stringify(prepareForExport ? prepareEntryForExport(logEntry, lastEntry) : logEntry) + '\n'
);
lastEntry = logEntry;
}
} catch (e) {
continue;
}
} while (true);
}
}
async function initializeRecentLogProvider() {
const logs = [];
for (const file of await getLogFiles(startOfDay(new Date()), new Date())) {
const fileStream = fs.createReadStream(file);
const reader = new LineReader(fileStream);
do {
const line = await reader.readLine();
if (line == null) break;
try {
const logEntry = JSON.parse(line);
logs.push(logEntry);
if (logs.length > RECENT_LOG_LIMIT) {
logs.shift();
}
} catch (e) {
continue;
}
} while (true);
}
recentLogs = logs;
recentLogs.push(...beforeRecentLogs);
}
let counter = 0;
function pushToRecentLogs(msg) {
const finalMsg = {
...msg,
counter,
};
counter += 1;
if (recentLogs) {
recentLogs.push(finalMsg);
adjustRecentLogs();
socket.emit('applog-event', finalMsg);
} else {
beforeRecentLogs.push(finalMsg);
}
}
function getRecentAppLogRecords() {
return recentLogs ?? beforeRecentLogs;
}
module.exports = {
initializeRecentLogProvider,
getRecentAppLogRecords,
pushToRecentLogs,
copyAppLogsIntoFile,
};
@@ -12,7 +12,7 @@ function childProcessChecker() {
// This will come once parent dies.
// One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
// and call process.exit()
logger.error(extractErrorLogData(err), 'parent died');
logger.error(extractErrorLogData(err), 'DBGM-00163 parent died');
process.exit(1);
}
}, 1000);
+82 -9
View File
@@ -13,11 +13,12 @@ const socket = require('./socket');
const config = require('../controllers/config');
const simpleEncryptor = require('simple-encryptor');
const currentVersion = require('../currentVersion');
const { getPublicIpInfo } = require('./hardwareFingerprint');
const logger = getLogger('cloudIntf');
let cloudFiles = null;
let promoWidgetData = null;
let promoWidgetDataLoaded = false;
const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY
? 'http://localhost:3103'
@@ -77,7 +78,7 @@ function startCloudTokenChecking(sid, callback) {
callback(resp.data);
}
} catch (err) {
logger.error(extractErrorLogData(err), 'Error checking cloud token');
logger.error(extractErrorLogData(err), 'DBGM-00164 Error checking cloud token');
}
}, 500);
}
@@ -125,7 +126,7 @@ async function getCloudUsedEngines() {
const resp = await callCloudApiGet('content-engines');
return resp || [];
} catch (err) {
logger.error(extractErrorLogData(err), 'Error getting cloud content list');
logger.error(extractErrorLogData(err), 'DBGM-00165 Error getting cloud content list');
return [];
}
}
@@ -200,20 +201,18 @@ async function updateCloudFiles(isRefresh) {
lastCloudFilesTags = '';
}
const ipInfo = await getPublicIpInfo();
const tags = (await collectCloudFilesSearchTags()).join(',');
let lastCheckedTm = 0;
if (tags == lastCloudFilesTags && cloudFiles.length > 0) {
lastCheckedTm = _.max(cloudFiles.map(x => parseInt(x.modifiedTm)));
}
logger.info({ tags, lastCheckedTm }, 'Downloading cloud files');
logger.info({ tags, lastCheckedTm }, 'DBGM-00082 Downloading cloud files');
const resp = await axios.default.get(
`${DBGATE_CLOUD_URL}/public-cloud-updates?lastCheckedTm=${lastCheckedTm}&tags=${tags}&isRefresh=${
isRefresh ? 1 : 0
}&country=${ipInfo?.country || ''}`,
}`,
{
headers: {
...getLicenseHttpHeaders(),
@@ -223,7 +222,7 @@ async function updateCloudFiles(isRefresh) {
}
);
logger.info(`Downloaded ${resp.data.length} cloud files`);
logger.info(`DBGM-00083 Downloaded ${resp.data.length} cloud files`);
const filesByPath = lastCheckedTm == 0 ? {} : _.keyBy(cloudFiles, 'path');
for (const file of resp.data) {
@@ -262,6 +261,45 @@ async function getPublicFileData(path) {
return resp.data;
}
async function ensurePromoWidgetDataLoaded() {
if (promoWidgetDataLoaded) {
return;
}
try {
const fileContent = await fs.readFile(path.join(datadir(), 'promo-widget.json'), 'utf-8');
promoWidgetData = JSON.parse(fileContent);
} catch (err) {
promoWidgetData = null;
}
promoWidgetDataLoaded = true;
}
async function updatePremiumPromoWidget() {
await ensurePromoWidgetDataLoaded();
const tags = (await collectCloudFilesSearchTags()).join(',');
const resp = await axios.default.get(
`${DBGATE_CLOUD_URL}/premium-promo-widget?identifier=${promoWidgetData?.identifier ?? 'empty'}&tags=${tags}`,
{
headers: {
...getLicenseHttpHeaders(),
...(await getCloudInstanceHeaders()),
'x-app-version': currentVersion.version,
},
}
);
if (!resp.data || resp.data?.state == 'unchanged') {
return;
}
promoWidgetData = resp.data;
await fs.writeFile(path.join(datadir(), 'promo-widget.json'), JSON.stringify(promoWidgetData, null, 2));
socket.emitChanged(`promo-widget-changed`);
}
async function refreshPublicFiles(isRefresh) {
if (!cloudFiles) {
await loadCloudFiles();
@@ -269,7 +307,11 @@ async function refreshPublicFiles(isRefresh) {
try {
await updateCloudFiles(isRefresh);
} catch (err) {
logger.error(extractErrorLogData(err), 'Error updating cloud files');
logger.error(extractErrorLogData(err), 'DBGM-00166 Error updating cloud files');
}
const configSettings = await config.get();
if (!isProApp() || configSettings?.trialDaysLeft != null) {
await updatePremiumPromoWidget();
}
}
@@ -423,6 +465,33 @@ function removeCloudCachedConnection(folid, cntid) {
delete cloudConnectionCache[cacheKey];
}
async function getPublicIpInfo() {
try {
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/ipinfo`);
if (!resp.data?.ip) {
return { ip: 'unknown-ip' };
}
return resp.data;
} catch (err) {
return { ip: 'unknown-ip' };
}
}
async function getPromoWidgetData() {
await ensurePromoWidgetDataLoaded();
return promoWidgetData;
}
async function getPromoWidgetPreview(campaign, variant) {
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/premium-promo-widget-preview/${campaign}/${variant}`);
return resp.data;
}
async function getPromoWidgetList() {
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/promo-widget-list`);
return resp.data;
}
module.exports = {
createDbGateIdentitySession,
startCloudTokenChecking,
@@ -439,4 +508,8 @@ module.exports = {
removeCloudCachedConnection,
readCloudTokenHolder,
readCloudTestTokenHolder,
getPublicIpInfo,
getPromoWidgetData,
getPromoWidgetPreview,
getPromoWidgetList,
};
+1 -1
View File
@@ -132,7 +132,7 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
connection.ssl = await extractConnectionSslParams(connection);
const conn = await driver.connect({ ...connection, ...additionalOptions });
const conn = await driver.connect({ conid: connectionLoaded?._id, ...connection, ...additionalOptions });
return conn;
}
+2 -2
View File
@@ -14,11 +14,11 @@ const createDirectories = {};
const ensureDirectory = (dir, clean) => {
if (!createDirectories[dir]) {
if (clean && fs.existsSync(dir) && !platformInfo.isForkedApi) {
getLogger('directories').info(`Cleaning directory ${dir}`);
getLogger('directories').info(`DBGM-00170 Cleaning directory ${dir}`);
cleanDirectory(dir, _.isNumber(clean) ? clean : null);
}
if (!fs.existsSync(dir)) {
getLogger('directories').info(`Creating directory ${dir}`);
getLogger('directories').info(`DBGM-00171 Creating directory ${dir}`);
fs.mkdirSync(dir);
}
createDirectories[dir] = true;
@@ -42,13 +42,13 @@ function extractSingleFileFromZip(zipPath, fileInZip, outputPath) {
// When the file is finished writing, resolve
writeStream.on('finish', () => {
logger.info(`File "${fileInZip}" extracted to "${outputPath}".`);
logger.info(`DBGM-00088 File "${fileInZip}" extracted to "${outputPath}".`);
resolve(true);
});
// Handle write errors
writeStream.on('error', writeErr => {
logger.error(extractErrorLogData(writeErr), `Error extracting "${fileInZip}" from "${zipPath}".`);
logger.error(extractErrorLogData(writeErr), `DBGM-00089 Error extracting "${fileInZip}" from "${zipPath}".`);
reject(writeErr);
});
});
@@ -67,7 +67,7 @@ function extractSingleFileFromZip(zipPath, fileInZip, outputPath) {
// Handle general errors
zipFile.on('error', err => {
logger.error(extractErrorLogData(err), `ZIP file error in ${zipPath}.`);
logger.error(extractErrorLogData(err), `DBGM-00172 ZIP file error in ${zipPath}.`);
reject(err);
});
});
+1 -1
View File
@@ -53,7 +53,7 @@ const getChartExport = (title, config, imageFile, plugins) => {
</div>
<div class="footer">
Exported from <a href='https://dbgate.io/' target='_blank'>DbGate</a>, powered by <a href='https://www.chartjs.org/' target='_blank'>Chart.js</a>
Exported from <a href='https://www.dbgate.io/' target='_blank'>DbGate</a>, powered by <a href='https://www.chartjs.org/' target='_blank'>Chart.js</a>
</div>
</body>
+1 -1
View File
@@ -18,7 +18,7 @@ const getMapExport = (geoJson) => {
leaflet
.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '<a href="https://dbgate.io" title="Exported from DbGate">DbGate</a> | © OpenStreetMap',
attribution: '<a href="https://www.dbgate.io" title="Exported from DbGate">DbGate</a> | © OpenStreetMap',
})
.addTo(map);
+18 -3
View File
@@ -14,9 +14,10 @@ class QueryStreamTableWriter {
this.currentChangeIndex = 1;
this.initializedFile = false;
this.sesid = sesid;
this.started = new Date().getTime();
}
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false) {
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false, options = {}) {
this.jslid = crypto.randomUUID();
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
fs.writeFileSync(
@@ -24,6 +25,7 @@ class QueryStreamTableWriter {
JSON.stringify({
...structure,
__isStreamHeader: true,
...options
}) + '\n'
);
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
@@ -118,6 +120,13 @@ class QueryStreamTableWriter {
this.chartProcessor = null;
}
}
process.send({
msgtype: 'endrecordset',
jslid: this.jslid,
rowCount: this.currentRowCount,
sesid: this.sesid,
durationMs: new Date().getTime() - this.started,
});
resolve();
});
} else {
@@ -148,6 +157,7 @@ class StreamHandler {
// this.error = this.error.bind(this);
this.done = this.done.bind(this);
this.info = this.info.bind(this);
this.changedCurrentDatabase = this.changedCurrentDatabase.bind(this);
// use this for cancelling - not implemented
// this.stream = null;
@@ -166,7 +176,11 @@ class StreamHandler {
}
}
recordset(columns) {
changedCurrentDatabase(database) {
process.send({ msgtype: 'changedCurrentDatabase', database, sesid: this.sesid });
}
recordset(columns, options) {
if (this.rowsLimitOverflow) {
return;
}
@@ -176,7 +190,8 @@ class StreamHandler {
Array.isArray(columns) ? { columns } : columns,
this.queryStreamInfoHolder.resultIndex,
this.frontMatter?.[`chart-${this.queryStreamInfoHolder.resultIndex + 1}`],
this.autoDetectCharts
this.autoDetectCharts,
options
);
this.queryStreamInfoHolder.resultIndex += 1;
this.rowCounter = 0;
@@ -3,18 +3,6 @@ const os = require('os');
const crypto = require('crypto');
const platformInfo = require('./platformInfo');
async function getPublicIpInfo() {
try {
const resp = await axios.default.get('https://ipinfo.io/json');
if (!resp.data?.ip) {
return { ip: 'unknown-ip' };
}
return resp.data;
} catch (err) {
return { ip: 'unknown-ip' };
}
}
function getMacAddress() {
try {
const interfaces = os.networkInterfaces();
@@ -32,6 +20,7 @@ function getMacAddress() {
}
async function getHardwareFingerprint() {
const { getPublicIpInfo } = require('./cloudIntf');
const publicIpInfo = await getPublicIpInfo();
const macAddress = getMacAddress();
const platform = os.platform();
@@ -42,8 +31,6 @@ async function getHardwareFingerprint() {
return {
publicIp: publicIpInfo.ip,
country: publicIpInfo.country,
region: publicIpInfo.region,
city: publicIpInfo.city,
macAddress,
platform,
release,
@@ -68,9 +55,7 @@ async function getPublicHardwareFingerprint() {
hash,
payload: {
platform: fingerprint.platform,
city: fingerprint.city,
country: fingerprint.country,
region: fingerprint.region,
isDocker: platformInfo.isDocker,
isAwsUbuntuLayout: platformInfo.isAwsUbuntuLayout,
isAzureUbuntuLayout: platformInfo.isAzureUbuntuLayout,
@@ -87,5 +72,4 @@ module.exports = {
getHardwareFingerprint,
getHardwareFingerprintHash,
getPublicHardwareFingerprint,
getPublicIpInfo,
};
+336 -72
View File
@@ -1,96 +1,350 @@
const { compilePermissions, testPermission } = require('dbgate-tools');
const { compilePermissions, testPermission, getPermissionsCacheKey } = require('dbgate-tools');
const _ = require('lodash');
const { getAuthProviderFromReq } = require('../auth/authProvider');
const cachedPermissions = {};
function hasPermission(tested, req) {
async function loadPermissionsFromRequest(req) {
const authProvider = getAuthProviderFromReq(req);
if (!req) {
// request object not available, allow all
return null;
}
const loadedPermissions = await authProvider.getCurrentPermissions(req);
return loadedPermissions;
}
function hasPermission(tested, loadedPermissions) {
if (!loadedPermissions) {
// not available, allow all
return true;
}
const permissions = getAuthProviderFromReq(req).getCurrentPermissions(req);
if (!cachedPermissions[permissions]) {
cachedPermissions[permissions] = compilePermissions(permissions);
const permissionsKey = getPermissionsCacheKey(loadedPermissions);
if (!cachedPermissions[permissionsKey]) {
cachedPermissions[permissionsKey] = compilePermissions(loadedPermissions);
}
return testPermission(tested, cachedPermissions[permissions]);
// const { user } = (req && req.auth) || {};
// const { login } = (process.env.OAUTH_PERMISSIONS && req && req.user) || {};
// const key = user || login || '';
// const logins = getLogins();
// if (!userPermissions[key]) {
// if (logins) {
// const login = logins.find(x => x.login == user);
// userPermissions[key] = compilePermissions(login ? login.permissions : null);
// } else {
// userPermissions[key] = compilePermissions(process.env.PERMISSIONS);
// }
// }
// return testPermission(tested, userPermissions[key]);
return testPermission(tested, cachedPermissions[permissionsKey]);
}
// let loginsCache = null;
// let loginsLoaded = false;
// function getLogins() {
// if (loginsLoaded) {
// return loginsCache;
// }
// const res = [];
// if (process.env.LOGIN && process.env.PASSWORD) {
// res.push({
// login: process.env.LOGIN,
// password: process.env.PASSWORD,
// permissions: process.env.PERMISSIONS,
// });
// }
// if (process.env.LOGINS) {
// const logins = _.compact(process.env.LOGINS.split(',').map(x => x.trim()));
// for (const login of logins) {
// const password = process.env[`LOGIN_PASSWORD_${login}`];
// const permissions = process.env[`LOGIN_PERMISSIONS_${login}`];
// if (password) {
// res.push({
// login,
// password,
// permissions,
// });
// }
// }
// } else if (process.env.OAUTH_PERMISSIONS) {
// const login_permission_keys = Object.keys(process.env).filter(key => _.startsWith(key, 'LOGIN_PERMISSIONS_'));
// for (const permissions_key of login_permission_keys) {
// const login = permissions_key.replace('LOGIN_PERMISSIONS_', '');
// const permissions = process.env[permissions_key];
// userPermissions[login] = compilePermissions(permissions);
// }
// }
// loginsCache = res.length > 0 ? res : null;
// loginsLoaded = true;
// return loginsCache;
// }
function connectionHasPermission(connection, req) {
function connectionHasPermission(connection, loadedPermissions) {
if (!connection) {
return true;
}
if (_.isString(connection)) {
return hasPermission(`connections/${connection}`, req);
return hasPermission(`connections/${connection}`, loadedPermissions);
} else {
return hasPermission(`connections/${connection._id}`, req);
return hasPermission(`connections/${connection._id}`, loadedPermissions);
}
}
function testConnectionPermission(connection, req) {
if (!connectionHasPermission(connection, req)) {
throw new Error('Connection permission not granted');
async function testConnectionPermission(connection, req, loadedPermissions) {
if (!loadedPermissions) {
loadedPermissions = await loadPermissionsFromRequest(req);
}
if (process.env.STORAGE_DATABASE) {
if (hasPermission(`all-connections`, loadedPermissions)) {
return;
}
const conid = _.isString(connection) ? connection : connection?._id;
if (hasPermission('internal-storage', loadedPermissions) && conid == '__storage') {
return;
}
const authProvider = getAuthProviderFromReq(req);
if (!req) {
return;
}
if (!(await authProvider.checkCurrentConnectionPermission(req, conid))) {
throw new Error('DBGM-00263 Connection permission not granted');
}
} else {
if (!connectionHasPermission(connection, loadedPermissions)) {
throw new Error('DBGM-00264 Connection permission not granted');
}
}
}
async function loadDatabasePermissionsFromRequest(req) {
const authProvider = getAuthProviderFromReq(req);
if (!req) {
return null;
}
const databasePermissions = await authProvider.getCurrentDatabasePermissions(req);
return databasePermissions;
}
async function loadTablePermissionsFromRequest(req) {
const authProvider = getAuthProviderFromReq(req);
if (!req) {
return null;
}
const tablePermissions = await authProvider.getCurrentTablePermissions(req);
return tablePermissions;
}
async function loadFilePermissionsFromRequest(req) {
const authProvider = getAuthProviderFromReq(req);
if (!req) {
return null;
}
const filePermissions = await authProvider.getCurrentFilePermissions(req);
return filePermissions;
}
function matchDatabasePermissionRow(conid, database, permissionRow) {
if (permissionRow.connection_id) {
if (conid != permissionRow.connection_id) {
return false;
}
}
if (permissionRow.database_names_list) {
const items = permissionRow.database_names_list.split('\n');
if (!items.find(item => item.trim()?.toLowerCase() === database?.toLowerCase())) {
return false;
}
}
if (permissionRow.database_names_regex) {
const regex = new RegExp(permissionRow.database_names_regex, 'i');
if (!regex.test(database)) {
return false;
}
}
return true;
}
function matchTablePermissionRow(objectTypeField, schemaName, pureName, permissionRow) {
if (permissionRow.table_names_list) {
const items = permissionRow.table_names_list.split('\n');
if (!items.find(item => item.trim()?.toLowerCase() === pureName?.toLowerCase())) {
return false;
}
}
if (permissionRow.table_names_regex) {
const regex = new RegExp(permissionRow.table_names_regex, 'i');
if (!regex.test(pureName)) {
return false;
}
}
if (permissionRow.schema_names_list) {
const items = permissionRow.schema_names_list.split('\n');
if (!items.find(item => item.trim()?.toLowerCase() === schemaName?.toLowerCase())) {
return false;
}
}
if (permissionRow.schema_names_regex) {
const regex = new RegExp(permissionRow.schema_names_regex, 'i');
if (!regex.test(schemaName)) {
return false;
}
}
return true;
}
function matchFilePermissionRow(folder, file, permissionRow) {
if (permissionRow.folder_name) {
if (folder != permissionRow.folder_name) {
return false;
}
}
if (permissionRow.file_names_list) {
const items = permissionRow.file_names_list.split('\n');
if (!items.find(item => item.trim()?.toLowerCase() === file?.toLowerCase())) {
return false;
}
}
if (permissionRow.file_names_regex) {
const regex = new RegExp(permissionRow.file_names_regex, 'i');
if (!regex.test(file)) {
return false;
}
}
return true;
}
const DATABASE_ROLE_ID_NAMES = {
'-1': 'view',
'-2': 'read_content',
'-3': 'write_data',
'-4': 'run_script',
'-5': 'deny',
};
const FILE_ROLE_ID_NAMES = {
'-1': 'allow',
'-2': 'deny',
};
function getDatabaseRoleLevelIndex(roleName) {
if (!roleName) {
return 6;
}
if (roleName == 'run_script') {
return 5;
}
if (roleName == 'write_data') {
return 4;
}
if (roleName == 'read_content') {
return 3;
}
if (roleName == 'view') {
return 2;
}
if (roleName == 'deny') {
return 1;
}
return 6;
}
function getTablePermissionRoleLevelIndex(roleName) {
if (!roleName) {
return 6;
}
if (roleName == 'run_script') {
return 5;
}
if (roleName == 'create_update_delete') {
return 4;
}
if (roleName == 'update_only') {
return 3;
}
if (roleName == 'read') {
return 2;
}
if (roleName == 'deny') {
return 1;
}
return 6;
}
function getDatabasePermissionRole(conid, database, loadedDatabasePermissions) {
let res = 'deny';
for (const permissionRow of loadedDatabasePermissions) {
if (!matchDatabasePermissionRow(conid, database, permissionRow)) {
continue;
}
res = DATABASE_ROLE_ID_NAMES[permissionRow.database_permission_role_id];
}
return res;
}
function getFilePermissionRole(folder, file, loadedFilePermissions) {
let res = 'deny';
for (const permissionRow of loadedFilePermissions) {
if (!matchFilePermissionRow(folder, file, permissionRow)) {
continue;
}
res = FILE_ROLE_ID_NAMES[permissionRow.file_permission_role_id];
}
return res;
}
const TABLE_ROLE_ID_NAMES = {
'-1': 'read',
'-2': 'update_only',
'-3': 'create_update_delete',
'-4': 'run_script',
'-5': 'deny',
};
const TABLE_SCOPE_ID_NAMES = {
'-1': 'all_objects',
'-2': 'tables',
'-3': 'views',
'-4': 'tables_views_collections',
'-5': 'procedures',
'-6': 'functions',
'-7': 'triggers',
'-8': 'sql_objects',
'-9': 'collections',
};
function getTablePermissionRole(
conid,
database,
objectTypeField,
schemaName,
pureName,
loadedTablePermissions,
databasePermissionRole
) {
let res =
databasePermissionRole == 'read_content'
? 'read'
: databasePermissionRole == 'write_data'
? 'create_update_delete'
: databasePermissionRole == 'run_script'
? 'run_script'
: 'deny';
for (const permissionRow of loadedTablePermissions) {
if (!matchDatabasePermissionRow(conid, database, permissionRow)) {
continue;
}
if (!matchTablePermissionRow(objectTypeField, schemaName, pureName, permissionRow)) {
continue;
}
const scope = TABLE_SCOPE_ID_NAMES[permissionRow.table_permission_scope_id];
switch (scope) {
case 'tables':
if (objectTypeField != 'tables') continue;
break;
case 'views':
if (objectTypeField != 'views') continue;
break;
case 'tables_views_collections':
if (objectTypeField != 'tables' && objectTypeField != 'views' && objectTypeField != 'collections') continue;
break;
case 'procedures':
if (objectTypeField != 'procedures') continue;
break;
case 'functions':
if (objectTypeField != 'functions') continue;
break;
case 'triggers':
if (objectTypeField != 'triggers') continue;
break;
case 'sql_objects':
if (objectTypeField != 'procedures' && objectTypeField != 'functions' && objectTypeField != 'triggers')
continue;
break;
case 'collections':
if (objectTypeField != 'collections') continue;
break;
}
res = TABLE_ROLE_ID_NAMES[permissionRow.table_permission_role_id];
}
return res;
}
async function testStandardPermission(permission, req, loadedPermissions) {
if (!loadedPermissions) {
loadedPermissions = await loadPermissionsFromRequest(req);
}
if (!hasPermission(permission, loadedPermissions)) {
throw new Error(`DBGM-00265 Permission ${permission} not granted`);
}
}
async function testDatabaseRolePermission(conid, database, requiredRole, req) {
if (!process.env.STORAGE_DATABASE) {
return;
}
const loadedPermissions = await loadPermissionsFromRequest(req);
if (hasPermission(`all-databases`, loadedPermissions)) {
return;
}
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const role = getDatabasePermissionRole(conid, database, databasePermissions);
const requiredIndex = getDatabaseRoleLevelIndex(requiredRole);
const roleIndex = getDatabaseRoleLevelIndex(role);
if (roleIndex < requiredIndex) {
throw new Error(`DBGM-00266 Permission ${requiredRole} not granted`);
}
}
@@ -98,4 +352,14 @@ module.exports = {
hasPermission,
connectionHasPermission,
testConnectionPermission,
loadPermissionsFromRequest,
loadDatabasePermissionsFromRequest,
loadTablePermissionsFromRequest,
loadFilePermissionsFromRequest,
getDatabasePermissionRole,
getTablePermissionRole,
getFilePermissionRole,
testStandardPermission,
testDatabaseRolePermission,
getTablePermissionRoleLevelIndex,
};
@@ -28,7 +28,7 @@ async function loadModelTransform(file) {
}
return null;
} catch (err) {
logger.error(extractErrorLogData(err), `Error loading model transform ${file}`);
logger.error(extractErrorLogData(err), `DBGM-00173 Error loading model transform ${file}`);
return null;
}
}
+7 -7
View File
@@ -40,7 +40,7 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
tunnelConfig,
});
} catch (err) {
logger.error(extractErrorLogData(err), 'Error connecting SSH');
logger.error(extractErrorLogData(err), 'DBGM-00174 Error connecting SSH');
}
return new Promise((resolve, reject) => {
let promiseHandled = false;
@@ -57,18 +57,18 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
}
});
subprocess.on('exit', code => {
logger.info(`SSH forward process exited with code ${code}`);
logger.info(`DBGM-00090 SSH forward process exited with code ${code}`);
delete sshTunnelCache[tunnelCacheKey];
if (!promiseHandled) {
reject(
new Error(
'SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'
'DBGM-00091 SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'
)
);
}
});
subprocess.on('error', error => {
logger.error(extractErrorLogData(error), 'SSH forward process error');
logger.error(extractErrorLogData(error), 'DBGM-00092 SSH forward process error');
delete sshTunnelCache[tunnelCacheKey];
if (!promiseHandled) {
reject(error);
@@ -97,13 +97,13 @@ async function getSshTunnel(connection) {
};
try {
logger.info(
`Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
`DBGM-00093 Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
);
const subprocess = await callForwardProcess(connection, tunnelConfig, tunnelCacheKey);
logger.info(
`Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
`DBGM-00094 Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
);
sshTunnelCache[tunnelCacheKey] = {
@@ -114,7 +114,7 @@ async function getSshTunnel(connection) {
};
return sshTunnelCache[tunnelCacheKey];
} catch (err) {
logger.error(extractErrorLogData(err), 'Error creating SSH tunnel:');
logger.error(extractErrorLogData(err), 'DBGM-00095 Error creating SSH tunnel:');
// error is not cached
return {
state: 'error',
+1 -1
View File
@@ -10,7 +10,7 @@ async function handleGetSshTunnelRequest({ msgid, connection }, subprocess) {
try {
subprocess.send({ msgtype: 'getsshtunnel-response', msgid, response });
} catch (err) {
logger.error(extractErrorLogData(err), 'Error sending to SSH tunnel');
logger.error(extractErrorLogData(err), 'DBGM-00175 Error sending to SSH tunnel');
}
}
+3 -3
View File
@@ -12,11 +12,11 @@ module.exports = function useController(app, electron, route, controller) {
const router = express.Router();
if (controller._init) {
logger.info(`Calling init controller for controller ${route}`);
logger.info(`DBGM-00096 Calling init controller for controller ${route}`);
try {
controller._init();
} catch (err) {
logger.error(extractErrorLogData(err), `Error initializing controller, exiting application`);
logger.error(extractErrorLogData(err), `DBGM-00097 Error initializing controller, exiting application`);
process.exit(1);
}
}
@@ -78,7 +78,7 @@ module.exports = function useController(app, electron, route, controller) {
const data = await controller[key]({ ...req.body, ...req.query }, req);
res.json(data);
} catch (err) {
logger.error(extractErrorLogData(err), `Error when processing route ${route}/${key}`);
logger.error(extractErrorLogData(err), `DBGM-00176 Error when processing route ${route}/${key}`);
if (err instanceof MissingCredentialsError) {
res.json({
missingCredentials: true,
+4
View File
@@ -3,6 +3,10 @@
"name": "dbgate-datalib",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"scripts": {
"build": "tsc",
"test": "jest",
+5 -5
View File
@@ -330,7 +330,7 @@ class ReplicatorItemHolder {
if (new Date().getTime() - lastLogged.getTime() > 5000) {
logger.info(
`Replicating ${this.item.name} in progress, inserted ${inserted} rows, mapped ${mapped} rows, missing ${missing} rows, skipped ${skipped} rows, updated ${updated} rows`
`DBGM-00105 Replicating ${this.item.name} in progress, inserted ${inserted} rows, mapped ${mapped} rows, missing ${missing} rows, skipped ${skipped} rows, updated ${updated} rows`
);
lastLogged = new Date();
}
@@ -489,19 +489,19 @@ export class DataReplicator {
for (const item of this.itemPlan) {
const stats = await item.runImport();
logger.info(
`Replicated ${item.name}, inserted ${stats.inserted} rows, mapped ${stats.mapped} rows, missing ${stats.missing} rows, skipped ${stats.skipped} rows, updated ${stats.updated} rows, deleted ${stats.deleted} rows`
`DBGM-00106 Replicated ${item.name}, inserted ${stats.inserted} rows, mapped ${stats.mapped} rows, missing ${stats.missing} rows, skipped ${stats.skipped} rows, updated ${stats.updated} rows, deleted ${stats.deleted} rows`
);
}
} catch (err) {
logger.error(extractErrorLogData(err), `Failed replicator job, rollbacking. ${err.message}`);
logger.error(extractErrorLogData(err), `DBGM-00179 Failed replicator job, rollbacking. ${err.message}`);
await this.runDumperCommand(dmp => dmp.rollbackTransaction());
return;
}
if (this.options.rollbackAfterFinish) {
logger.info('Rollbacking transaction, nothing was changed');
logger.info('DBGM-00107 Rollbacking transaction, nothing was changed');
await this.runDumperCommand(dmp => dmp.rollbackTransaction());
} else {
logger.info('Committing replicator transaction');
logger.info('DBGM-00108 Committing replicator transaction');
await this.runDumperCommand(dmp => dmp.commitTransaction());
}
+3
View File
@@ -31,6 +31,8 @@ export interface GridConfig extends GridConfigColumns {
formFilterColumns: string[];
multiColumnFilter?: string;
searchInColumns?: string;
disabledFilterColumns: string[];
disabledMultiColumnFilter?: boolean;
}
export interface GridCache {
@@ -48,6 +50,7 @@ export function createGridConfig(): GridConfig {
focusedColumns: null,
grouping: {},
formFilterColumns: [],
disabledFilterColumns: [],
};
}
+41 -3
View File
@@ -13,7 +13,7 @@ import type {
FilterBehaviour,
} from 'dbgate-types';
import { parseFilter } from 'dbgate-filterparser';
import { filterName } from 'dbgate-tools';
import { filterName, shortenIdentifier } from 'dbgate-tools';
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
import { Expression, Select, treeToSql, dumpSqlSelect, Condition, CompoudCondition } from 'dbgate-sqltree';
import { isTypeLogical, standardFilterBehaviours, detectSqlFilterBehaviour, stringFilterBehaviour } from 'dbgate-tools';
@@ -24,6 +24,7 @@ export interface DisplayColumn {
columnName: string;
headerText: string;
uniqueName: string;
uniqueNameShorten?: string;
uniquePath: string[];
notNull?: boolean;
autoIncrement?: boolean;
@@ -232,6 +233,7 @@ export abstract class GridDisplay {
if (!filter) continue;
const column = displayedColumnInfo[uniqueName];
if (!column) continue;
if (this.isFilterDisabled(uniqueName)) continue;
try {
const condition = parseFilter(
filter,
@@ -258,7 +260,7 @@ export abstract class GridDisplay {
}
}
if (this.baseTableOrView && this.config.multiColumnFilter) {
if (this.baseTableOrView && this.config.multiColumnFilter && !this.isMultiColumnFilterDisabled()) {
const orCondition: CompoudCondition = {
conditionType: 'or',
conditions: [],
@@ -415,6 +417,7 @@ export abstract class GridDisplay {
[uniqueName]: value,
},
formViewRecordNumber: 0,
disabledFilterColumns: cfg.disabledFilterColumns.filter(x => x != uniqueName),
}));
this.reload();
}
@@ -424,6 +427,7 @@ export abstract class GridDisplay {
...cfg,
multiColumnFilter: value,
formViewRecordNumber: 0,
disabledMultiColumnFilter: false,
}));
this.reload();
}
@@ -447,6 +451,7 @@ export abstract class GridDisplay {
...cfg,
filters: _.omit(cfg.filters, [uniqueName]),
formFilterColumns: (cfg.formFilterColumns || []).filter(x => x != uniqueName),
disabledFilterColumns: (cfg.disabledFilterColumns).filter(x => x != uniqueName),
}));
this.reload();
}
@@ -462,6 +467,37 @@ export abstract class GridDisplay {
this.reload();
}
toggleFilterEnabled(uniqueName) {
if (this.isFilterDisabled(uniqueName)) {
this.setConfig(cfg => ({
...cfg,
disabledFilterColumns: cfg.disabledFilterColumns.filter(x => x != uniqueName),
}));
} else {
this.setConfig(cfg => ({
...cfg,
disabledFilterColumns: [...cfg.disabledFilterColumns, uniqueName],
}));
}
this.reload();
}
isFilterDisabled(uniqueName: string) {
return this.config.disabledFilterColumns.includes(uniqueName);
}
toggleMultiColumnFilterEnabled() {
this.setConfig(cfg => ({
...cfg,
disabledMultiColumnFilter: !cfg.disabledMultiColumnFilter,
}));
this.reload();
}
isMultiColumnFilterDisabled() {
return this.config.disabledMultiColumnFilter;
}
setSort(uniqueName, order) {
this.setConfig(cfg => ({
...cfg,
@@ -606,7 +642,9 @@ export abstract class GridDisplay {
}
return {
exprType: 'column',
...(!this.dialect.omitTableAliases && { alias: alias || col.columnName }),
...(!this.dialect.omitTableAliases && {
alias: alias ?? col.columnName,
}),
source,
...col,
};
+4 -4
View File
@@ -43,11 +43,11 @@ export class ScriptDrivedDeployer {
dmp.put('select * from ~dbgate_deploy_journal')
);
this.journalItems = rows;
logger.debug(`Loaded ${rows.length} items from DbGate deploy journal`);
logger.debug(`DBGM-00109 Loaded ${rows.length} items from DbGate deploy journal`);
} catch (err) {
logger.warn(
extractErrorLogData(err),
'Error loading DbGate deploy journal, creating table dbgate_deploy_journal'
'DBGM-00110 Error loading DbGate deploy journal, creating table dbgate_deploy_journal'
);
const dmp = this.driver.createDumper();
dmp.createTable({
@@ -126,12 +126,12 @@ export class ScriptDrivedDeployer {
runCommandOnDriver(this.dbhan, this.driver, dmp => dmp.beginTransaction());
}
logger.debug(`Running ${category} script ${file.name}`);
logger.debug(`DBGM-00111 Running ${category} script ${file.name}`);
try {
await this.driver.script(this.dbhan, file.text, { useTransaction: false });
await this.saveToJournal(file, category, hash);
} catch (err) {
logger.error(extractErrorLogData(err), `Error running ${category} script ${file.name}`);
logger.error(extractErrorLogData(err), `DBGM-00180 Error running ${category} script ${file.name}`);
if (this.driver.supportsTransactions) {
runCommandOnDriver(this.dbhan, this.driver, dmp => dmp.rollbackTransaction());
return;

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