Compare commits

...

1118 Commits

Author SHA1 Message Date
Stela Augustinova
c51dad39e0 Improve saveRows method to handle backpressure and errors in file writing 2026-04-14 09:49:14 +02:00
Stela Augustinova
1d350a3a29 Add validation to assertSafeArchiveName to prevent resolving to archive root 2026-04-13 14:55:29 +02:00
Stela Augustinova
81e3cce070 Add validation for linkedFolder in createLink method 2026-04-13 13:54:48 +02:00
Stela Augustinova
f9de2d77b5 Moved functionName validation 2026-04-13 13:44:45 +02:00
Stela Augustinova
3956eaf389 Improve error handling in unzipDirectory by adding readStream error listener and immediate abort on file extraction failure 2026-04-13 13:20:28 +02:00
Stela Augustinova
d13e2c2d87 Make fs.writeFile call awaitable in createLink method for proper async handling 2026-04-13 12:53:54 +02:00
Stela Augustinova
ebf2371da9 Enhance unzipDirectory error handling and manage active streams for better resource cleanup 2026-04-13 12:43:45 +02:00
Stela Augustinova
fa4b12448d Refactor unzipDirectory to improve error handling and prevent multiple rejections 2026-04-13 10:48:01 +02:00
Stela Augustinova
5fe6dfa551 Refactor loaderScriptTemplate to streamline plugin extraction and improve readability 2026-04-13 10:41:12 +02:00
Stela Augustinova
6061c8b0a5 Update JS reserved words 2026-04-13 10:31:12 +02:00
Stela Augustinova
1ac0aa8a3e Add path traversal and null byte checks for archive names and ZIP entries 2026-04-13 09:19:03 +02:00
Stela Augustinova
5d04d7f01f Enhance JavaScript identifier validation and update variable storage method in ScriptWriterEval 2026-04-10 16:15:45 +02:00
Stela Augustinova
9c97e347c5 Add validation for JavaScript identifiers and shell API function names 2026-04-10 13:22:54 +02:00
Stela Augustinova
22967d123d Add 7.1.8 entry to CHANGELOG with fixed NPM packages build 2026-04-09 16:05:50 +02:00
Stela Augustinova
3fed650254 Add postgresql optimalization to 7.1.7 changelog 2026-04-09 16:03:24 +02:00
Stela Augustinova
b57b2083d3 v7.1.8 2026-04-09 15:35:13 +02:00
Stela Augustinova
1f47e8c62e v7.1.8-alpha.7 2026-04-09 15:26:28 +02:00
CI workflows
d7ce653d74 chore: auto-update github workflows 2026-04-09 13:25:14 +00:00
Stela Augustinova
07c803efee Update npm publishing steps and remove unnecessary access flag 2026-04-09 15:24:48 +02:00
Stela Augustinova
26b6d9133e v7.1.8-alpha.6 2026-04-09 15:15:37 +02:00
CI workflows
146084bdb3 chore: auto-update github workflows 2026-04-09 13:14:54 +00:00
Stela Augustinova
fa82b4630b Specify npm version to 11.5.1 for consistency 2026-04-09 15:14:24 +02:00
Stela Augustinova
d00841030f v7.1.8-alpha.5 2026-04-09 15:01:14 +02:00
CI workflows
c517bb0be6 chore: auto-update github workflows 2026-04-09 13:00:00 +00:00
Stela Augustinova
e585d8be8f Add public access to npm publish commands in build workflow 2026-04-09 14:59:25 +02:00
Stela Augustinova
8be76832a5 v7.1.8-alpha.4 2026-04-09 14:54:23 +02:00
Stela Augustinova
99df266a3e v7.1.8-aplha.4 2026-04-09 14:50:56 +02:00
CI workflows
5660874992 chore: auto-update github workflows 2026-04-09 12:50:31 +00:00
Stela Augustinova
b0dade9da3 Configure NPM token in build workflow 2026-04-09 14:50:11 +02:00
Stela Augustinova
a533858804 v7.1.8-alpha.3 2026-04-09 14:20:39 +02:00
CI workflows
d3bcc984e7 chore: auto-update github workflows 2026-04-09 12:18:26 +00:00
Stela Augustinova
99e8307a80 Enable NPM token configuration in build workflow 2026-04-09 14:17:59 +02:00
Stela Augustinova
73926ea392 v7.1.8-alpha.2 2026-04-09 14:12:17 +02:00
CI workflows
5ff24526b7 chore: auto-update github workflows 2026-04-09 12:11:15 +00:00
Stela Augustinova
32ed1c57bd Update Node.js setup to use yarn caching and remove npm install step 2026-04-09 14:10:50 +02:00
Stela Augustinova
f4c3a95348 v7.1.8-alpha.1 2026-04-09 14:02:38 +02:00
CI workflows
b1a908343a chore: auto-update github workflows 2026-04-09 11:58:25 +00:00
Stela Augustinova
7f9d7eb36e Update Node.js setup action and enable npm caching 2026-04-09 13:57:51 +02:00
Stela Augustinova
30820e29fc Update CHANGELOG for version 7.1.7 2026-04-09 13:23:07 +02:00
Stela Augustinova
a85ea2e0f7 v7.1.7 2026-04-09 12:56:57 +02:00
Stela Augustinova
993e713955 v7.1.7-premium-beta.5 2026-04-09 12:11:02 +02:00
Stela Augustinova
3151e30db1 SYNC: Update translations 2026-04-09 08:59:26 +00:00
Jan Prochazka
eb5219dd68 Merge pull request #1422 from dbgate/feature/duplicate-translation-keys
Remove duplicate translation keys
2026-04-09 10:49:30 +02:00
Stela Augustinova
bb44783369 Refactor translation keys to eliminate duplicates in QueryTab component 2026-04-09 10:33:33 +02:00
CI workflows
33b46c4db3 chore: auto-update github workflows 2026-04-09 08:24:34 +00:00
Jan Prochazka
3730aae62a Merge pull request #1419 from dbgate/feature/map-referer
Added referer
2026-04-09 10:24:25 +02:00
CI workflows
065062d58a Update pro ref 2026-04-09 08:24:16 +00:00
Jan Prochazka
7b2f58e68e SYNC: Merge pull request #92 from dbgate/feature/ai-toggle 2026-04-09 08:24:02 +00:00
Stela Augustinova
e2fc23fcf8 Remove duplicate translation keys 2026-04-09 10:12:39 +02:00
SPRINX0\prochazka
6f56ef284d v7.1.7-premium-beta.4 2026-04-08 16:14:19 +02:00
SPRINX0\prochazka
08a644ba39 v7.1.7-premuim-beta.4 2026-04-08 16:07:40 +02:00
CI workflows
6ae19ac4a6 chore: auto-update github workflows 2026-04-08 14:06:22 +00:00
CI workflows
7761cbe81d Update pro ref 2026-04-08 14:05:57 +00:00
Jan Prochazka
f981d88150 SYNC: Merge pull request #91 from dbgate/feature/query-history-per-user 2026-04-08 14:05:40 +00:00
CI workflows
e2a23eaa0d chore: auto-update github workflows 2026-04-08 12:57:03 +00:00
CI workflows
9d510b3c08 Update pro ref 2026-04-08 12:56:40 +00:00
SPRINX0\prochazka
a98f5ac45e reverted yarn.lock 2026-04-08 14:03:13 +02:00
SPRINX0\prochazka
b989e964c0 v7.1.7-premium-beta.3 2026-04-08 13:34:11 +02:00
CI workflows
3ff6eefa06 chore: auto-update github workflows 2026-04-08 11:29:47 +00:00
CI workflows
67fde9be3c Update pro ref 2026-04-08 11:29:28 +00:00
SPRINX0\prochazka
df7ac89723 SYNC: v7.1.7-premium-beta.2 2026-04-08 11:29:18 +00:00
SPRINX0\prochazka
358df9f53b SYNC: try to fix ms entra login 2026-04-08 11:29:15 +00:00
Stela Augustinova
02e3bfaa8a Added referer 2026-04-08 12:05:42 +02:00
Jan Prochazka
dde74fa73b Merge pull request #1407 from dbgate/feature/postgres-optimalization
Feature/postgres optimalization
2026-04-08 11:46:42 +02:00
SPRINX0\prochazka
100e3fe75f deleted sast workflows 2026-04-08 10:59:29 +02:00
SPRINX0\prochazka
af7930cea2 Enhance aggregation functions in SQL queries for improved PostgreSQL compatibility 2026-04-08 10:55:24 +02:00
SPRINX0\prochazka
6b4f6b909c Merge branch 'feature/postgres-optimalization' of https://github.com/dbgate/dbgate into feature/postgres-optimalization 2026-04-08 10:26:35 +02:00
Jan Prochazka
9a6e5cd7cc Update plugins/dbgate-plugin-postgres/src/backend/sql/views.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-08 10:21:22 +02:00
SPRINX0\prochazka
9f64b6ec7a Merge branch 'master' into feature/postgres-optimalization 2026-04-08 10:20:28 +02:00
Stela Augustinova
77f720e34c Refactor connection handling in extractShellConnection to improve volatile ID management and ensure secure credential handling 2026-04-08 10:20:09 +02:00
Stela Augustinova
168dcb7824 Enhance error handling for connection requests in subprocesses and validate connection ID format 2026-04-08 10:20:09 +02:00
Stela Augustinova
759186a212 Improve error handling for volatile connection responses in subprocess communication 2026-04-08 10:20:09 +02:00
Stela Augustinova
71ed7a76ea Handle errors in volatile connection resolution and remove unused registration function 2026-04-08 10:20:09 +02:00
Stela Augustinova
bd939b22c7 Fix volatile connection resolution to prevent multiple resolves 2026-04-08 10:20:09 +02:00
Stela Augustinova
c327f77294 Refactor volatile connections handling in connections and runners modules 2026-04-08 10:20:09 +02:00
Stela Augustinova
d907d79beb Streamline volatile connections handling and remove unused registration module 2026-04-08 10:20:09 +02:00
Stela Augustinova
93b879927c Implement volatile connections handling in runners and shell modules 2026-04-08 10:20:09 +02:00
Stela Augustinova
0c545d4cf9 Enhance clipboard formatters to skip empty rows, improving data handling in clipboard operations 2026-04-08 10:20:09 +02:00
Stela Augustinova
95c90c1517 Improve clipboard formatters to omit undefined values, enhancing data integrity in exports 2026-04-08 10:20:09 +02:00
CI workflows
cb731fa858 chore: auto-update github workflows 2026-04-08 10:20:09 +02:00
Stela Augustinova
9bb3b09ecf SYNC: Add SAST workflow for security scanning using Semgrep 2026-04-08 10:20:09 +02:00
SPRINX0\prochazka
7c8f541d3e deleted sast workflow 2026-04-08 10:18:37 +02:00
Jan Prochazka
ce41687382 Merge pull request #1417 from dbgate/feature/auth-error
Implement volatile connections handling in runners and shell modules
2026-04-08 10:14:02 +02:00
Stela Augustinova
4b083dea5c Refactor connection handling in extractShellConnection to improve volatile ID management and ensure secure credential handling 2026-04-07 14:56:29 +02:00
Stela Augustinova
c84473c1eb Enhance error handling for connection requests in subprocesses and validate connection ID format 2026-04-07 14:26:58 +02:00
Stela Augustinova
7fc078f3e6 Improve error handling for volatile connection responses in subprocess communication 2026-04-07 14:15:18 +02:00
Stela Augustinova
cbbd538248 Handle errors in volatile connection resolution and remove unused registration function 2026-04-07 14:01:13 +02:00
Stela Augustinova
825f6e562b Fix volatile connection resolution to prevent multiple resolves 2026-04-07 13:46:34 +02:00
Stela Augustinova
a278afb260 Refactor volatile connections handling in connections and runners modules 2026-04-07 13:42:11 +02:00
Stela Augustinova
2fbeea717c Streamline volatile connections handling and remove unused registration module 2026-04-07 13:26:16 +02:00
Jan Prochazka
c7259e4663 Merge pull request #1412 from dbgate/feature/copy-sql
Improve clipboard formatters to omit undefined values, enhancing data…
2026-04-07 13:11:49 +02:00
Stela Augustinova
69a2669342 Implement volatile connections handling in runners and shell modules 2026-04-07 13:06:04 +02:00
CI workflows
42d1ca8fd4 chore: auto-update github workflows 2026-04-07 10:27:40 +00:00
Stela Augustinova
1cf52d8b39 SYNC: Add SAST workflow for security scanning using Semgrep 2026-04-07 10:27:24 +00:00
Jan Prochazka
6e482afab2 v7.1.7-premium-beta.1 2026-04-02 16:39:06 +02:00
SPRINX0\prochazka
ddf3295e6d Merge branch 'master' into feature/postgres-optimalization 2026-04-02 16:33:25 +02:00
SPRINX0\prochazka
79e087abd3 Optimize PostgreSQL analysis queries and add support for Info Schema routines 2026-04-02 16:32:36 +02:00
CI workflows
a7cf51bdf7 chore: auto-update github workflows 2026-04-02 13:55:33 +00:00
Jan Prochazka
dfdb31e2f8 Merge pull request #1413 from dbgate/feature/integration-test-pro
Update test workflow to include directory changes for integration tests
2026-04-02 15:55:14 +02:00
Stela Augustinova
3508ddc3ca Update test workflow to include directory changes for integration tests 2026-04-02 11:02:36 +02:00
Stela Augustinova
137fc6b928 Enhance clipboard formatters to skip empty rows, improving data handling in clipboard operations 2026-04-02 10:29:02 +02:00
Jan Prochazka
e6f5295420 Merge pull request #1410 from dbgate/feature/large-fields
Enhance binary size handling in grid cell display
2026-04-01 16:01:23 +02:00
CI workflows
2bb08921c3 chore: auto-update github workflows 2026-04-01 13:55:00 +00:00
Stela Augustinova
ee2d0e4c30 Remove unnecessary restart policy for DynamoDB service 2026-04-01 15:54:35 +02:00
Jan Prochazka
c43a838572 Merge pull request #1411 from dbgate/feature/unreadable-dropdown
Correct class binding and update style variables in SelectField compo…
2026-04-01 15:53:23 +02:00
CI workflows
17ff6a8013 chore: auto-update github workflows 2026-04-01 13:53:13 +00:00
Stela Augustinova
62ad6a0d08 Remove unnecessary restart policy for MongoDB service 2026-04-01 15:52:48 +02:00
CI workflows
5c049fa867 chore: auto-update github workflows 2026-04-01 13:51:09 +00:00
CI workflows
619f17114a Update pro ref 2026-04-01 13:50:58 +00:00
Stela Augustinova
1c1431014c SYNC: Merge pull request #87 from dbgate/feature/collection-test 2026-04-01 13:50:46 +00:00
Stela Augustinova
9d1d7b7e34 Improve clipboard formatters to omit undefined values, enhancing data integrity in exports 2026-04-01 15:49:35 +02:00
Stela Augustinova
f68ca1e786 Correct class binding and update style variables in SelectField component 2026-04-01 13:24:34 +02:00
Stela Augustinova
8d16a30064 Fix message formatting for large binary fields in stringifyCellValue function 2026-04-01 10:55:47 +02:00
Stela Augustinova
cf601c33c0 Enhance binary size handling in grid cell display 2026-04-01 10:25:40 +02:00
Jan Prochazka
588cd39d7c Merge pull request #1404 from dbgate/feature/fetch-all-button
Add fetch all button
2026-04-01 09:44:04 +02:00
Stela Augustinova
79ebfa9b7a Add fetchAll command to dataGrid menu 2026-03-31 13:37:06 +02:00
Stela Augustinova
0c6b2746d1 Fix file stream reference in jsldata and remove redundant buffer assignment in LoadingDataGridCore 2026-03-31 08:59:33 +02:00
Stela Augustinova
978972c55c Enhance file path validation in streamRows to include symlink resolution and case normalization, improving security and error handling 2026-03-31 08:31:43 +02:00
Stela Augustinova
37854fc577 Refactor fetchAll to trim lines before parsing, improving error handling for malformed data 2026-03-31 06:54:37 +02:00
Stela Augustinova
5537e193a6 Improve fetchAll error handling and cleanup process during streaming and paginated reads 2026-03-31 06:21:06 +02:00
Stela Augustinova
0d42b2b133 Refactor fetchAll cancel function to improve cleanup process and prevent errors 2026-03-30 15:48:35 +02:00
Stela Augustinova
44bd7972d4 Enhance fetchAll functionality with improved error handling and state management 2026-03-30 14:34:57 +02:00
Stela Augustinova
5143eb39f7 Implement fetchAll functionality with streaming support and error handling 2026-03-30 13:30:12 +02:00
Stela Augustinova
cf51883b3e Add checkbox to skip confirmation when fetching all rows 2026-03-26 15:24:25 +01:00
Stela Augustinova
484ca0c78a Reset loaded time reference in reload function 2026-03-26 15:11:11 +01:00
Stela Augustinova
8f5cad0e2c Prevent loading next data when fetching all rows is in progress 2026-03-26 15:03:54 +01:00
Stela Augustinova
988512a571 Update warning message in FetchAllConfirmModal to simplify language 2026-03-26 14:50:09 +01:00
Stela Augustinova
f8bd380051 Optimize fetchAllRows by using a local buffer to reduce array copies and improve performance 2026-03-26 14:19:11 +01:00
Stela Augustinova
281131dbba Enhance fetchAll functionality by adding loading state check 2026-03-26 14:07:12 +01:00
Stela Augustinova
ea3a61077a v7.1.6 2026-03-26 12:47:09 +01:00
Stela Augustinova
d1a898b40d SYNC: Add translations for cloudUnavailable message in multiple languages 2026-03-26 11:11:07 +00:00
Stela Augustinova
a521a81ef0 v7.1.6-premium-beta.1 2026-03-26 11:25:13 +01:00
Stela Augustinova
2505c61975 Add fetch all button 2026-03-26 11:24:05 +01:00
Stela Augustinova
ab5a54dbb6 SYNC: Merge pull request #89 from dbgate/feature/cloud-error 2026-03-26 10:12:05 +00:00
Stela Augustinova
44ad8fa60a Update CHANGELOG for version 7.1.5 2026-03-25 16:59:13 +01:00
Stela Augustinova
5b27a241d7 v7.1.5 2026-03-25 16:21:59 +01:00
Stela Augustinova
084019ca65 v7.1.5-premium-beta.3 2026-03-25 15:21:43 +01:00
Stela Augustinova
ba147af8fe SYNC: v7.1.5-premium-beta.2 2026-03-25 14:08:24 +00:00
Stela Augustinova
1b3f4db07d SYNC: Merge pull request #88 from dbgate/feature/cloud-error 2026-03-25 13:39:00 +00:00
Jan Prochazka
c36705d458 Merge pull request #1395 from dbgate/feature/display-uuid
Feature/display UUID
2026-03-25 10:04:58 +01:00
Stela Augustinova
0e126cb8cf Enhance BinData subType handling to support hexadecimal strings and improve validation 2026-03-25 08:32:03 +01:00
Stela Augustinova
c48183a539 Enhance base64 to UUID conversion with error handling and regex improvements 2026-03-25 08:23:15 +01:00
Stela Augustinova
50f380dbbe Enhance uuidToBase64 function with validation and improve UUID parsing in parseCellValue 2026-03-24 17:15:32 +01:00
Stela Augustinova
66023a9a68 Validate base64 UUID conversion and enhance handling in stringifyCellValue 2026-03-24 17:06:52 +01:00
Stela Augustinova
c3fbc3354c Validate BinData subType to ensure it is an integer between 0 and 255 2026-03-24 16:32:16 +01:00
Jan Prochazka
a7d2ed11f3 SYNC: Merge pull request #86 from dbgate/feature/icon-vulnerability 2026-03-23 12:50:27 +00:00
SPRINX0\prochazka
899aec2658 v7.1.5-premium-beta.1 2026-03-20 14:24:11 +01:00
SPRINX0\prochazka
74e47587e2 Merge branch 'master' into feature/postgres-optimalization 2026-03-20 14:23:40 +01:00
Stela Augustinova
6a3dc92572 Add uuid to base64 conversion and enhance cell value parsing for UUIDs 2026-03-20 12:46:50 +01:00
Stela Augustinova
e3a4667422 feat: add base64 to UUID conversion and integrate into cell value parsing 2026-03-19 14:50:08 +01:00
Stela Augustinova
c4dd99bba9 Changelog 7.1.4 2026-03-19 13:07:44 +01:00
SPRINX0\prochazka
cb70f3c318 postgres loading optimalization 2026-03-19 12:17:29 +01:00
Stela Augustinova
588b6f9882 v7.1.4 2026-03-19 12:13:37 +01:00
Stela Augustinova
375f69ca1e v7.1.4-alpha.2 2026-03-19 11:13:29 +01:00
Stela Augustinova
a32e5cc139 v7.1.4-alpha.1 2026-03-19 10:56:16 +01:00
CI workflows
8e00137751 chore: auto-update github workflows 2026-03-19 09:33:56 +00:00
Stela Augustinova
003db50833 SYNC: Add missing publish step for rest 2026-03-19 09:33:36 +00:00
Stela Augustinova
bc519c2c20 Changelog 7.1.3 2026-03-18 16:06:01 +01:00
Stela Augustinova
3b41fa8cfa v7.1.3 2026-03-18 15:31:26 +01:00
Stela Augustinova
39ed0f6d2d v7.1.3-premium-beta.7 2026-03-18 14:27:27 +01:00
CI workflows
710f796832 chore: auto-update github workflows 2026-03-18 13:15:43 +00:00
CI workflows
9ec5fb7263 Update pro ref 2026-03-18 13:15:24 +00:00
Stela Augustinova
407db457d5 SYNC: Added new translations and error codes 2026-03-18 13:15:12 +00:00
Jan Prochazka
0c5d2cfcd1 Merge pull request #1393 from dbgate/feature/script-filter
Add cloud content list integration for connection label resolution
2026-03-18 13:55:40 +01:00
CI workflows
87ace375bb chore: auto-update github workflows 2026-03-18 12:54:58 +00:00
CI workflows
d010020f3b Update pro ref 2026-03-18 12:54:34 +00:00
Jan Prochazka
c60227a98f SYNC: Merge pull request #85 from dbgate/feature/proxy-configuration 2026-03-18 12:54:21 +00:00
Stela Augustinova
2824681bff Refactor cloudIdToLabel assignment to use lodash's fromPairs for improved readability 2026-03-18 13:47:45 +01:00
Stela Augustinova
073a3e3946 Add cloud content list integration for connection label resolution 2026-03-18 11:23:31 +01:00
CI workflows
93e91127a0 chore: auto-update github workflows 2026-03-18 08:03:38 +00:00
CI workflows
b60a6cff56 Update pro ref 2026-03-18 08:03:23 +00:00
Jan Prochazka
1f3b1963d9 SYNC: errors assign 2026-03-18 08:03:13 +00:00
SPRINX0\prochazka
4915f57abb v7.1.3-premium-beta.6 2026-03-17 15:35:35 +01:00
Jan Prochazka
97c6fc97d5 Merge pull request #1392 from dbgate/feature/duckdb-integration-test
Synchronize client and instance disconnection methods
2026-03-17 15:34:51 +01:00
Stela Augustinova
b68421bbc3 Synchronize client and instance disconnection methods 2026-03-17 14:45:57 +01:00
SPRINX0\prochazka
2d10559754 v7.1.3-premium-beta.5 2026-03-17 13:38:35 +01:00
CI workflows
b398a7b546 chore: auto-update github workflows 2026-03-17 11:58:40 +00:00
CI workflows
1711d2102d Update pro ref 2026-03-17 11:58:24 +00:00
Jan Prochazka
97cea230f3 SYNC: Merge pull request #83 from dbgate/feature/transaction-isolation 2026-03-17 11:58:10 +00:00
CI workflows
b6a0fe9465 chore: auto-update github workflows 2026-03-17 11:46:56 +00:00
CI workflows
06c50659bb Update pro ref 2026-03-17 11:46:39 +00:00
Jan Prochazka
244b47f548 SYNC: Merge pull request #84 from dbgate/feature/proxy-configuration 2026-03-17 11:46:28 +00:00
Jan Prochazka
b72a244d93 Merge pull request #1389 from dbgate/feature/duckdb-query-result
Fix getColumnsInfo loop to iterate from start to end
2026-03-17 09:55:59 +01:00
Jan Prochazka
c1e069d4dc Merge pull request #1391 from dbgate/feature/script-filter
Refactor connection selection to use a dropdown instead of a button f…
2026-03-17 09:50:01 +01:00
Stela Augustinova
f99994085a Refactor connection selection to use a dropdown instead of a button for improved usability 2026-03-17 09:22:18 +01:00
Stela Augustinova
32fd0dd78c Update @duckdb/node-api dependency to version 1.5.0-r.1 2026-03-16 15:52:01 +01:00
Jan Prochazka
a557b6b2b4 Merge pull request #1388 from dbgate/feature/script-filter
Feature/script filter
2026-03-16 15:27:49 +01:00
Stela Augustinova
e84583c776 Fix getColumnsInfo loop to iterate from start to end 2026-03-16 15:09:31 +01:00
Stela Augustinova
a548b0d543 Refactor connection label assignment to use logical OR for fallback 2026-03-16 15:05:45 +01:00
Stela Augustinova
de94f15383 Fix file reading to correctly handle bytes read from file 2026-03-16 14:41:38 +01:00
Stela Augustinova
7045d986ef Fix file handle management to ensure proper closure in file reading process 2026-03-16 14:31:43 +01:00
Stela Augustinova
de7ae9cf09 Refactor connection filter options 2026-03-16 14:17:06 +01:00
Stela Augustinova
ab3d6888dc Enhance file reading and connection filtering in SavedFilesList component 2026-03-16 14:08:19 +01:00
Stela Augustinova
98a70891f3 Refactor file reading 2026-03-16 08:12:35 +01:00
Stela Augustinova
52e7326a2c Enhance file listing to support front matter parsing and connection filtering 2026-03-16 08:02:03 +01:00
Jan Prochazka
bfd2e3b07a Merge pull request #1382 from dbgate/feature/add-files-button
Enhance drag-and-drop functionality to support Electron file paths
2026-03-12 12:41:31 +01:00
Stela Augustinova
799f5e30d3 Enhance drag-and-drop functionality to support Electron file paths 2026-03-12 10:14:47 +01:00
SPRINX0\prochazka
d3e544c3c0 v7.1.3-premium-beta.4 2026-03-11 08:55:53 +01:00
CI workflows
866fd55834 chore: auto-update github workflows 2026-03-10 10:17:13 +00:00
CI workflows
74ce1fba32 Update pro ref 2026-03-10 10:16:57 +00:00
Jan Prochazka
a11b93b4cc SYNC: Merge pull request #80 from dbgate/feature/loading-fix 2026-03-10 10:16:46 +00:00
CI workflows
066f2baa03 chore: auto-update github workflows 2026-03-10 09:50:36 +00:00
Stela Augustinova
e02396280f SYNC: Add port mappings for DynamoDB and fix formatting in e2e-pro.yaml 2026-03-10 09:50:18 +00:00
CI workflows
a654c80746 chore: auto-update github workflows 2026-03-10 09:32:53 +00:00
CI workflows
3b50f4bd7c Update pro ref 2026-03-10 09:32:34 +00:00
CI workflows
cc1f77f5bc chore: auto-update github workflows 2026-03-10 08:23:51 +00:00
CI workflows
381fce4a82 Update pro ref 2026-03-10 08:23:35 +00:00
Jan Prochazka
bc3be97cee SYNC: Merge pull request #81 from dbgate/feature/dynamo-e2e 2026-03-10 08:22:32 +00:00
Jan Prochazka
1c389208a7 Merge pull request #1378 from dbgate/feature/add-files-button
Import getElectron in ElectronFilesInput component
2026-03-10 09:19:34 +01:00
SPRINX0\prochazka
cbeed2d3d0 v7.1.3-alpha.3 2026-03-09 10:20:49 +01:00
SPRINX0\prochazka
3d974ad144 v7.1.3-alpha.2 2026-03-09 10:01:50 +01:00
SPRINX0\prochazka
749042a05d set version 2026-03-09 09:59:53 +01:00
SPRINX0\prochazka
52413b82ee v7.1.3-alpha.1 2026-03-09 09:22:26 +01:00
SPRINX0\prochazka
212a7ec083 used exact version 2026-03-09 09:21:57 +01:00
SPRINX0\prochazka
cee94fe113 added missing package 2026-03-09 09:20:48 +01:00
Stela Augustinova
e1ead2519a Import getElectron in ElectronFilesInput component 2026-03-09 07:35:34 +01:00
Jan Prochazka
80330a25ac Merge pull request #1372 from dbgate/feature/export-diagram
Add diagram export to png
2026-03-05 10:32:35 +01:00
Stela Augustinova
508470e970 Added import 2026-03-05 10:02:57 +01:00
Stela Augustinova
bc64b4b5c7 Update ToolStripDropDownButton label to use translation for export 2026-03-04 15:36:40 +01:00
Jan Prochazka
48d8494ead SYNC: added CLAUDE.md 2026-03-04 07:42:30 +00:00
SPRINX0\prochazka
2a51d2ed96 SYNC: fix: enhance date handling in zipDataRow function 2026-03-03 16:13:49 +00:00
Stela Augustinova
cfabcc7bf6 Fix import name for ToolStripDropDownButton in DiagramTab.svelte 2026-03-03 17:08:13 +01:00
Stela Augustinova
90fc8fd0fc Add diagram export to png 2026-03-03 16:54:46 +01:00
SPRINX0\prochazka
ff54533e33 v7.1.2 2026-03-02 15:53:28 +01:00
SPRINX0\prochazka
2072f0b5ba SYNC: don't use random data in testing REST service 2026-03-02 14:10:12 +00:00
Jan Prochazka
6efc720a45 SYNC: Merge pull request #78 from dbgate/feature/aitest 2026-03-02 13:28:57 +00:00
SPRINX0\prochazka
c7cb1efe9c v7.1.2-premium-beta.2 2026-03-02 12:59:39 +01:00
SPRINX0\prochazka
e193531246 changelog 2026-03-02 12:58:03 +01:00
CI workflows
2aa53f414e chore: auto-update github workflows 2026-03-02 11:57:43 +00:00
CI workflows
843c15d754 Update pro ref 2026-03-02 11:57:27 +00:00
SPRINX0\prochazka
fb19582088 v7.1.2-premium-beta.1 2026-03-02 10:34:53 +01:00
SPRINX0\prochazka
8040466cbe text 2026-03-02 10:34:16 +01:00
CI workflows
302b4d7acd chore: auto-update github workflows 2026-03-02 09:33:33 +00:00
CI workflows
a8ccc24d46 Update pro ref 2026-03-02 09:33:16 +00:00
Jan Prochazka
b2fb071a7b SYNC: Merge pull request #73 from dbgate/feature/openai-upgrade 2026-03-02 09:33:04 +00:00
SPRINX0\prochazka
204d7b97d5 chore: update CHANGELOG for version 7.1.1 enhancements and fixes 2026-02-27 16:08:33 +01:00
SPRINX0\prochazka
f3da709aac v7.1.1 2026-02-27 15:34:12 +01:00
SPRINX0\prochazka
0ab8afb838 v7.1.1-packer-beta.3 2026-02-27 13:36:37 +01:00
SPRINX0\prochazka
d50999547f v7.1.1-premium-beta.2 2026-02-27 13:36:14 +01:00
CI workflows
04741b0eba chore: auto-update github workflows 2026-02-27 12:35:44 +00:00
SPRINX0\prochazka
ba86fe32e7 comment out azure build 2026-02-27 13:35:24 +01:00
CI workflows
9deb7d7fdc chore: auto-update github workflows 2026-02-27 12:34:08 +00:00
CI workflows
55eb64e5ca Update pro ref 2026-02-27 12:33:52 +00:00
Jan Prochazka
a5f50f3f2b SYNC: Merge pull request #68 from dbgate/feature/dynamodb-plugin 2026-02-27 12:33:39 +00:00
Jan Prochazka
47214eb5b3 SYNC: Merge pull request #72 from dbgate/feature-firebird-fixes 2026-02-27 12:24:38 +00:00
CI workflows
599509d417 chore: auto-update github workflows 2026-02-27 08:20:08 +00:00
CI workflows
9d366fc359 Update pro ref 2026-02-27 08:19:53 +00:00
SPRINX0\prochazka
0e1ed0bde6 SYNC: upgraded dbgate-query-splitter 2026-02-27 08:19:41 +00:00
CI workflows
6ad7824bf2 chore: auto-update github workflows 2026-02-27 08:06:47 +00:00
CI workflows
1174f51c07 Update pro ref 2026-02-27 08:06:31 +00:00
Jan Prochazka
1950dda1ab SYNC: Merge pull request #70 from dbgate/feature/new-gql-query 2026-02-27 08:06:19 +00:00
Jan Prochazka
8231b6d5be SYNC: Merge pull request #71 from dbgate/feature/reset-virtual-scroll 2026-02-27 08:03:36 +00:00
Jan Prochazka
0feacbe6eb Merge pull request #1368 from dbgate/feature/driver-selection
Set default selected item to 'general' in SettingsTab and WidgetIconP…
2026-02-27 08:21:16 +01:00
Jan Prochazka
80b5f5adca SYNC: Merge pull request #65 from dbgate/feature/filter-bigint 2026-02-26 08:48:23 +00:00
CI workflows
13650f36e6 chore: auto-update github workflows 2026-02-26 08:47:23 +00:00
CI workflows
3f58d99069 Update pro ref 2026-02-26 08:47:03 +00:00
CI workflows
0c8a025cf6 chore: auto-update github workflows 2026-02-26 08:33:59 +00:00
CI workflows
5014df4859 Update pro ref 2026-02-26 08:33:42 +00:00
SPRINX0\prochazka
34a491e2ef v7.1.1-premium-beta.1 2026-02-25 14:07:03 +01:00
Jan Prochazka
884e4ca88e SYNC: Merge pull request #67 from dbgate/feature/connfix 2026-02-25 13:01:09 +00:00
CI workflows
a670c5e86c chore: auto-update github workflows 2026-02-25 12:54:58 +00:00
CI workflows
af1fba79be Update pro ref 2026-02-25 12:54:40 +00:00
Jan Prochazka
ac44de0bf4 SYNC: Merge pull request #66 from dbgate/team-premium-permis-fix-2 2026-02-25 12:54:28 +00:00
Stela Augustinova
f013a241ce Merge pull request #1367 from dbgate/feature/disable-cell-data-view
Add setting to disable automatic Cell Data View opening
2026-02-25 10:31:42 +01:00
Stela Augustinova
0e29a7206d Prevent unnecessary updates in handleUserChange when the selected item remains unchanged 2026-02-25 09:27:50 +01:00
Stela Augustinova
689b3f299c Prevent unnecessary updates in handleUserChange when the selected item remains unchanged 2026-02-25 09:20:08 +01:00
Stela Augustinova
02ccb990bd Remove default selected item from SettingsTab in stdCommands and WidgetIconPanel 2026-02-25 09:18:51 +01:00
Stela Augustinova
61fe4f0d57 Set default selected item to 'general' in SettingsTab and WidgetIconPanel 2026-02-25 09:09:17 +01:00
Stela Augustinova
0a920195d5 Add setting to disable automatic Cell Data View opening 2026-02-25 07:14:31 +01:00
SPRINX0\prochazka
18896bf56d v7.1.0 2026-02-24 15:18:24 +01:00
SPRINX0\prochazka
098c9041a0 changelog 2026-02-24 15:15:05 +01:00
CI workflows
61a41d8eb2 chore: auto-update github workflows 2026-02-24 13:40:14 +00:00
CI workflows
e76073d5c8 Update pro ref 2026-02-24 13:39:55 +00:00
Jan Prochazka
8c34added7 SYNC: Merge pull request #63 from dbgate/feature/test-api-e2e 2026-02-24 13:39:42 +00:00
SPRINX0\prochazka
66fc6b93ae v7.0.7-premium-beta.13 2026-02-24 13:17:35 +01:00
SPRINX0\prochazka
881d5a8008 v7.0.7-beta.12 2026-02-24 12:51:25 +01:00
Jan Prochazka
5d263de954 SYNC: Merge pull request #62 from dbgate/feature/refactor-rolldown 2026-02-24 11:50:42 +00:00
SPRINX0\prochazka
c8d0494000 v7.0.7-beta.11 2026-02-24 12:29:12 +01:00
SPRINX0\prochazka
a9b48b5aa5 v7.0.7-premium-beta.10 2026-02-24 12:25:37 +01:00
SPRINX0\prochazka
f08a951eef SYNC: Refactor DriverSettings and stores to manage hidden database engines 2026-02-24 11:23:14 +00:00
SPRINX0\prochazka
8758a4bc86 SYNC: filter extensions in active drivers 2026-02-24 10:13:49 +00:00
Jan Prochazka
aae328f8c8 Merge pull request #1365 from dbgate/feature/driver-selection
Feature/driver selection
2026-02-24 11:07:43 +01:00
Stela Augustinova
1953578a33 Fix driver reference checks in DriverSettings and stores for improved stability 2026-02-24 11:02:38 +01:00
Stela Augustinova
543bdd79d9 Fix filter syntax in ConnectionDriverFields to improve driver selection logic 2026-02-24 10:50:11 +01:00
Stela Augustinova
e0e1a3c8e4 Enhance DriverSettings component to handle undefined drivers and improve check-all functionality 2026-02-24 10:33:30 +01:00
Stela Augustinova
f1d84f448e Refactor FormConnectionTypeSelector to improve layout and integrate FontIcon in driver settings button 2026-02-24 10:14:32 +01:00
Jan Prochazka
7c5c21f15d SYNC: Merge pull request #56 from dbgate/feature/flipping-tabs-crash 2026-02-24 09:05:43 +00:00
Jan Prochazka
41ffaeebe3 Merge pull request #1362 from david-pivonka/fix/clickhouse-cte-results
fix(clickhouse): show query results for CTE (WITH) queries
2026-02-24 09:59:48 +01:00
SPRINX0\prochazka
5d9b44b647 SYNC: removed experimental flags 2026-02-24 08:26:44 +00:00
CI workflows
a18d2c5650 chore: auto-update github workflows 2026-02-24 08:21:49 +00:00
CI workflows
e0379bcf12 Update pro ref 2026-02-24 08:21:35 +00:00
SPRINX0\prochazka
e91242d5a2 SYNC: use string instead of datatime in password_reset_token (for compatibility) 2026-02-24 08:21:23 +00:00
SPRINX0\prochazka
8177187b3a v7.0.7-premium-beta.9 2026-02-24 08:57:50 +01:00
CI workflows
6b3e1144bc chore: auto-update github workflows 2026-02-24 07:56:50 +00:00
CI workflows
dfec88f52d Update pro ref 2026-02-24 07:56:34 +00:00
SPRINX0\prochazka
b8df67659a v7.0.7-premium-beta.8 2026-02-24 08:25:26 +01:00
SPRINX0\prochazka
861da64581 fix 2026-02-24 08:24:14 +01:00
CI workflows
ab147a2cc9 chore: auto-update github workflows 2026-02-24 07:20:26 +00:00
CI workflows
e13191e894 Update pro ref 2026-02-24 07:20:09 +00:00
SPRINX0\prochazka
7f69ea8dc0 SYNC: fixed links (dbgate.org => dbgate.io) 2026-02-24 07:19:58 +00:00
SPRINX0\prochazka
ef2140696b publish NPM plugins 2026-02-24 08:13:05 +01:00
SPRINX0\prochazka
4607900c3b SYNC: yarn.lock 2026-02-24 07:05:31 +00:00
CI workflows
3258d55796 chore: auto-update github workflows 2026-02-24 06:58:55 +00:00
CI workflows
35e6966c39 Update pro ref 2026-02-24 06:58:38 +00:00
SPRINX0\prochazka
885756b259 removed ADD plugin 2026-02-24 07:57:10 +01:00
Jan Prochazka
5fbc1b937c SYNC: Merge pull request #55 from dbgate/feature/dynamodb-plugin 2026-02-24 06:51:31 +00:00
CI workflows
7e444e9fc2 chore: auto-update github workflows 2026-02-24 06:35:37 +00:00
CI workflows
c051237914 Update pro ref 2026-02-24 06:35:20 +00:00
Jan Prochazka
3855b0dd28 SYNC: Merge pull request #61 from dbgate/feature/gql-variables-mutation 2026-02-24 06:35:09 +00:00
Stela Augustinova
afcc9e096a Add FormConnectionTypeSelector component and integrate into ConnectionDriverFields 2026-02-23 15:48:46 +01:00
Stela Augustinova
f4df1fbff4 Add separator line in DriverSettings for improved UI clarity 2026-02-23 15:33:12 +01:00
Jan Prochazka
45b3a5af91 SYNC: Merge pull request #60 from dbgate/feature/redis-key-loading 2026-02-23 12:43:04 +00:00
Stela Augustinova
f54b18e652 Refactor DriverSettings component to enhance check-all functionality and improve UI layout 2026-02-23 07:52:42 +01:00
Stela Augustinova
b1210d19ad Add DriverSettings component and integrate into SettingsTab 2026-02-20 18:13:56 +01:00
CI workflows
21cbcc79c6 chore: auto-update github workflows 2026-02-20 14:33:44 +00:00
CI workflows
a7d0c8fb0f Update pro ref 2026-02-20 14:33:26 +00:00
SPRINX0\prochazka
1e3dc54d81 v7.0.7-premium-beta.7 2026-02-20 13:25:12 +01:00
CI workflows
48f294fd83 chore: auto-update github workflows 2026-02-20 12:22:39 +00:00
CI workflows
298ad0de4b Update pro ref 2026-02-20 12:22:17 +00:00
Jan Prochazka
c7953f9231 SYNC: Merge pull request #59 from dbgate/feature/graphql-connection-view 2026-02-20 12:22:05 +00:00
SPRINX0\prochazka
afd97eae7d v7.0.7-premium-beta.6 2026-02-19 11:00:36 +01:00
Jan Prochazka
f4e558b7e8 SYNC: Merge pull request #58 from dbgate/feature/array-grid-improvements 2026-02-19 09:58:30 +00:00
SPRINX0\prochazka
12c99c646e v7.0.7-premium-beta.5 2026-02-18 19:11:40 +01:00
CI workflows
6c1a2eedbe chore: auto-update github workflows 2026-02-18 17:52:44 +00:00
CI workflows
8a73216035 Update pro ref 2026-02-18 17:52:25 +00:00
Jan Prochazka
c6a93f12f7 SYNC: Merge pull request #57 from dbgate/feature/improve-api-capabilities 2026-02-18 17:52:13 +00:00
CI workflows
09f44d94b3 chore: auto-update github workflows 2026-02-18 15:32:46 +00:00
CI workflows
c26748154a Update pro ref 2026-02-18 15:32:30 +00:00
Jan Prochazka
2474f915d4 SYNC: Merge pull request #54 from dbgate/feature/odata-api 2026-02-18 15:32:17 +00:00
Jan Prochazka
53f940cd23 SYNC: Merge pull request #52 from dbgate/feature/group-by-timestamp 2026-02-18 14:45:25 +00:00
CI workflows
991b648854 chore: auto-update github workflows 2026-02-18 07:23:25 +00:00
CI workflows
663f057a9a Update pro ref 2026-02-18 07:23:08 +00:00
Jan Prochazka
61963fb824 SYNC: Merge pull request #53 from dbgate/feature/graphql-connection-display 2026-02-18 07:22:55 +00:00
SPRINX0\prochazka
bdf3cf5b36 SYNC: Enhance GraphQL query parsing to include argument values and update related components to handle new structure 2026-02-17 13:46:14 +00:00
CI workflows
5cc459594b chore: auto-update github workflows 2026-02-17 13:22:07 +00:00
CI workflows
8d315e52df Update pro ref 2026-02-17 13:21:50 +00:00
SPRINX0\prochazka
48a24a8704 SYNC: Add support for GraphQL connection queries and enhance API type handling 2026-02-17 13:21:38 +00:00
SPRINX0\prochazka
cdce52f0e5 v7.0.7-premium-beta.4 2026-02-17 10:46:42 +01:00
SPRINX0\prochazka
d12ccbeac4 SYNC: Reduce default maximum depth for GraphQL explorer options from 6 to 2 2026-02-17 09:46:01 +00:00
David Pivoňka
0b1620105a fix(clickhouse): show query results for CTE (WITH) queries
The stream() method used a regex that only matched queries starting
with SELECT. Queries using CTEs (WITH ... SELECT) were incorrectly
sent through client.command() which discards results, causing the
query console to show "Query execution finished" with no data grid.

Update the regex to also match queries starting with WITH so they
flow through client.query() and display results correctly.

Fixes #1138

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 08:37:32 +01:00
CI workflows
2ae9c98acb chore: auto-update github workflows 2026-02-17 07:04:41 +00:00
CI workflows
ed00848a1e Update pro ref 2026-02-17 07:04:24 +00:00
SPRINX0\prochazka
06f7741dbf SYNC: Refactor REST authentication handling and improve connection utilities 2026-02-17 07:04:12 +00:00
CI workflows
8d3b7cace8 chore: auto-update github workflows 2026-02-16 16:03:32 +00:00
CI workflows
8f0775e337 Update pro ref 2026-02-16 16:03:16 +00:00
SPRINX0\prochazka
444cb6aa0c SYNC: errors assign 2026-02-16 16:03:05 +00:00
SPRINX0\prochazka
b4acc19ea2 v7.0.7-premium-beta.3 2026-02-16 16:56:20 +01:00
SPRINX0\prochazka
1ef17cd861 SYNC: Masking connections improved #1357 2026-02-16 15:48:21 +00:00
SPRINX0\prochazka
e564e930e5 env config 2026-02-16 16:23:07 +01:00
SPRINX0\prochazka
a30badbbe0 enhance connection masking #1357 2026-02-16 16:23:02 +01:00
SPRINX0\prochazka
b33d21fdb3 v7.0.7-beta.2 2026-02-16 16:11:56 +01:00
SPRINX0\prochazka
78da83f7db fix: optimize query string handling in executeRestApiEndpoint 2026-02-16 15:09:30 +01:00
SPRINX0\prochazka
8f6313d4ec fix: update build process to include dbgate-rest and adjust dependencies 2026-02-16 14:55:46 +01:00
Jan Prochazka
14962a5622 Merge pull request #1360 from dbgate/feature/numeric-sum
Numeric handling in DataGridCore
2026-02-16 14:32:41 +01:00
Jan Prochazka
b8048e7592 Merge pull request #1361 from dbgate/feature/mssql-fk-duplicate
Feature/mssql fk duplicate
2026-02-16 14:25:33 +01:00
SPRINX0\prochazka
cf9823e123 v7.0.7-beta.1 2026-02-16 14:17:16 +01:00
SPRINX0\prochazka
1667dbfde0 added REST fake methods/files 2026-02-16 14:16:41 +01:00
CI workflows
416436a612 chore: auto-update github workflows 2026-02-16 13:01:33 +00:00
CI workflows
dc1b724d8d Update pro ref 2026-02-16 13:01:17 +00:00
Jan Prochazka
080dc44175 SYNC: Merge pull request #51 from dbgate/feature/rest-poc 2026-02-16 13:00:56 +00:00
Stela Augustinova
be148297a2 Fix numeric precision in sum calculation 2026-02-16 13:26:47 +01:00
Stela Augustinova
6cf6d8c876 Use isPlainObject instead of isObject 2026-02-16 13:18:11 +01:00
Stela Augustinova
3921f50feb Removed unnecessary endCommand call in tableOptions method 2026-02-16 07:45:20 +01:00
Stela Augustinova
6fc63be56a fixed duplicate FK 2026-02-16 07:39:18 +01:00
Stela Augustinova
6a03f9a6fe Numeric handling in DataGridCore 2026-02-16 07:22:46 +01:00
00adrn
721fdf09b3 Added option to toggle database formats on and off in Settings->Connection menu. Now, when creating a new connection, only enabled database formats will appear. 2026-02-15 21:01:23 -05:00
SPRINX0\prochazka
bd4a52318b changelog 2026-02-13 09:58:44 +01:00
SPRINX0\prochazka
3978865902 v7.0.6 2026-02-13 09:55:26 +01:00
SPRINX0\prochazka
c1228ee426 v7.0.5 2026-02-13 09:55:08 +01:00
CI workflows
d0c39dc932 chore: auto-update github workflows 2026-02-13 08:50:50 +00:00
CI workflows
63a8586d7c Update pro ref 2026-02-13 08:50:33 +00:00
SPRINX0\prochazka
e0a79c033e SYNC: fix 2026-02-13 08:10:27 +00:00
SPRINX0\prochazka
fa12f127ce v7.0.5-premium-beta.2 2026-02-13 09:04:54 +01:00
SPRINX0\prochazka
10916eadd5 v7.0.5-beta.1 2026-02-13 08:50:20 +01:00
SPRINX0\prochazka
7f4e8e9c8f Crypting passwords when using SHELL_SCRIPTING=1 and env variables #1357 2026-02-13 08:31:14 +01:00
CI workflows
d06840f934 chore: auto-update github workflows 2026-02-10 16:10:26 +00:00
CI workflows
75f4df8b51 Update pro ref 2026-02-10 16:10:08 +00:00
Jan Prochazka
e9ea6d27ae SYNC: Merge pull request #49 from dbgate/feature/reset-password 2026-02-10 16:09:53 +00:00
SPRINX0\prochazka
48019d43c3 v7.0.4 2026-02-09 15:39:46 +01:00
SPRINX0\prochazka
04dbeb633d changelog 2026-02-09 15:39:34 +01:00
SPRINX0\prochazka
71631865c4 v7.0.4-premium-beta.3 2026-02-09 14:50:52 +01:00
Jan Prochazka
d4a39cf481 Merge pull request #1349 from dbgate/feature/mysql-indexes
MySQL FULLTEXT support #1305
2026-02-09 13:16:37 +01:00
SPRINX0\prochazka
3a71dfff64 removed sort by index type 2026-02-09 13:14:52 +01:00
Jan Prochazka
d8c865b3ce Update packages/tools/src/SqlDumper.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 13:11:01 +01:00
SPRINX0\prochazka
71356b798c fixed Error messages in Chinese will display garbled characters. #1321 2026-02-09 13:03:49 +01:00
SPRINX0\prochazka
66ddd1741f MySQL FULLTEXT support #1305 2026-02-09 12:50:18 +01:00
SPRINX0\prochazka
8a60f3c8a7 SYNC: fix: correct typo in numeric_scale variable in getColumnInfo function #1325 2026-02-09 11:07:46 +00:00
Jan Prochazka
25d2a40c50 SYNC: Merge pull request #48 from dbgate/feature/skip-group-by 2026-02-09 08:55:45 +00:00
SPRINX0\prochazka
17b389146c v7.0.4-premium-beta.2 2026-02-06 15:31:39 +01:00
CI workflows
bd1f609b39 chore: auto-update github workflows 2026-02-06 14:10:09 +00:00
CI workflows
01e1831d57 Update pro ref 2026-02-06 14:09:54 +00:00
Jan Prochazka
c341baa781 SYNC: Merge pull request #47 from dbgate/feature/color-refactor-2 2026-02-06 14:09:41 +00:00
Jan Prochazka
995a0c33c3 Merge pull request #1347 from dbgate/feature/color-refactor
Feature/color refactor
2026-02-06 13:42:33 +01:00
SPRINX0\prochazka
75845cb42d fix 2026-02-06 13:41:25 +01:00
SPRINX0\prochazka
822d6acfb0 dark usercolors changed 2026-02-06 13:05:05 +01:00
SPRINX0\prochazka
2c4510a717 color fixes 2026-02-06 13:01:58 +01:00
SPRINX0\prochazka
ac2391c91a connection color in statusbar refactor 2026-02-06 11:13:11 +01:00
SPRINX0\prochazka
e805563ce5 connection color refactor 2026-02-06 10:58:30 +01:00
Jan Prochazka
88fb1d920e Merge pull request #1345 from dbgate/feature/form-view-edit
Feature/form view edit
2026-02-06 09:51:13 +01:00
SPRINX0\prochazka
dca60fad7a Merge branch 'master' into feature/form-view-edit 2026-02-05 16:23:49 +01:00
SPRINX0\prochazka
8226b05e7e SYNC: fixed CSV export 2026-02-05 15:18:16 +00:00
Stela Augustinova
0106331978 Fix loop termination condition in cell selection logic for DataGrid 2026-02-05 15:54:56 +01:00
Stela Augustinova
f5c0e7d2e9 Refactor startEditing function to streamline editable and grider checks 2026-02-05 15:50:21 +01:00
Stela Augustinova
4568b24351 Enhance row selection logic to support primary key filtering in DataGrid 2026-02-05 15:44:46 +01:00
Stela Augustinova
042502f41f Refactor row selection handling to support multiple selected rows in DataGrid 2026-02-05 14:39:56 +01:00
Stela Augustinova
d342d73818 Refactor selection handling in DataGrid to improve row selection logic and UI responsiveness 2026-02-05 14:30:07 +01:00
SPRINX0\prochazka
3b922216c1 changelog 2026-02-05 13:32:12 +01:00
SPRINX0\prochazka
10fa9b6812 v7.0.3 2026-02-05 13:29:47 +01:00
SPRINX0\prochazka
33ccbf790b dark theme fix 2026-02-05 13:29:28 +01:00
SPRINX0\prochazka
50b4baee4b v7.0.2 2026-02-05 13:03:41 +01:00
CI workflows
be57a56095 chore: auto-update github workflows 2026-02-04 15:49:11 +00:00
SPRINX0\prochazka
f351453b9c uncommented azre build 2026-02-04 16:41:01 +01:00
SPRINX0\prochazka
dda67d3351 v7.0.2-premium-beta.3 2026-02-04 16:39:45 +01:00
SPRINX0\prochazka
23e1e744e8 changelog 2026-02-04 16:33:11 +01:00
Jan Prochazka
4b0affe182 SYNC: Merge pull request #46 from dbgate/feature/widget-panel 2026-02-04 14:57:25 +00:00
SPRINX0\prochazka
ab836bc747 v7.0.2-packer-beta.2 2026-02-04 15:24:55 +01:00
SPRINX0\prochazka
cf86d7e352 v7.0.2-packer-beta.1 2026-02-04 15:24:15 +01:00
CI workflows
05a36d3878 chore: auto-update github workflows 2026-02-04 14:23:09 +00:00
CI workflows
0417084a39 Update pro ref 2026-02-04 14:22:51 +00:00
SPRINX0\prochazka
cdfe39f226 SYNC: temporatrily switched off Axure build 2026-02-04 14:22:41 +00:00
SPRINX0\prochazka
b73dde3a48 code format 2026-02-04 13:40:46 +01:00
Jan Prochazka
0dea597226 Merge pull request #1342 from dbgate/feature/mssql-filter-text-empty
Feature/mssql filter text empty
2026-02-04 09:17:34 +01:00
SPRINX0\prochazka
2f38928c89 Refactor DATALENGTH handling for MSSQL empty string checks and update dialect interface 2026-02-04 09:08:31 +01:00
SPRINX0\prochazka
35c7b5e952 Add support for DATALENGTH in MSSQL for empty string checks 2026-02-04 08:56:19 +01:00
Stela Augustinova
ba28b17263 Add shortcut to clear cell value with Ctrl+0 in FormCellView 2026-02-03 15:52:05 +01:00
Stela Augustinova
51b0e004fa Update cell selection to handle bigint and decimal values in DataGrid 2026-02-03 10:50:14 +01:00
Stela Augustinova
8cb59b02a8 Enhance editing behavior to refocus editor if already editing the same column 2026-02-03 10:19:10 +01:00
Stela Augustinova
38bfd130a3 Reset current cell and selection when row count is zero after loading all data 2026-02-02 16:00:53 +01:00
Stela Augustinova
369d90e057 Clear cell selection after applying filter in datagrid 2026-02-02 14:36:32 +01:00
Jan Prochazka
c27cdd1734 Merge pull request #1340 from dbgate/feature/editor-icon-height
Adjust background size for ace gutter SQL run icon
2026-02-02 14:03:58 +01:00
Jan Prochazka
e7e4f39311 Merge pull request #1339 from dbgate/feature/csv-export
Feature/csv export
2026-02-02 14:03:38 +01:00
Stela Augustinova
0443a21e05 Add support for uppercase boolean format in CSV export 2026-02-02 10:41:36 +01:00
Stela Augustinova
50c01886ec Fixed text wrap in cell view 2026-02-02 09:05:37 +01:00
Stela Augustinova
a9e1219f6c Adjust background size for ace gutter SQL run icon 2026-01-30 17:59:37 +01:00
Stela Augustinova
7bc31dde70 Add boolean format option to CSV export configuration 2026-01-30 17:42:07 +01:00
Stela Augustinova
65f2f1d08f Add support for bigint and binary values in CSV export 2026-01-30 15:39:57 +01:00
Stela Augustinova
684027eaab Handle decimal and boolean values in CSV export 2026-01-30 14:59:08 +01:00
Jan Prochazka
1c3ec9c3bb SYNC: Merge pull request #45 from dbgate/feature/statusbar-colors 2026-01-30 06:33:36 +00:00
SPRINX0\prochazka
3a8ff2c05d SYNC: FIXED: Search for database in cloud connection #1329 2026-01-29 14:25:33 +00:00
SPRINX0\prochazka
d5bd179c68 SYNC: configurable toolbar position #1326 2026-01-29 09:54:01 +00:00
SPRINX0\prochazka
8b938a39cf SYNC: changed: prioritize map selection in CellDataWidget autodetection #1330 2026-01-29 08:09:41 +00:00
Jan Prochazka
c9610cbc39 SYNC: Merge pull request #44 from dbgate/feature/test-connection-msentra 2026-01-28 13:52:06 +00:00
SPRINX0\prochazka
931733d605 SYNC: Optimalized loading MySQL primary keys #1261 2026-01-28 07:29:54 +00:00
SPRINX0\prochazka
44e5d0e195 v7.0.1 2026-01-28 08:09:58 +01:00
SPRINX0\prochazka
08b83dc3fd changelog 2026-01-28 07:43:44 +01:00
SPRINX0\prochazka
b7f261a836 v7.0.1-premium-beta.5 2026-01-28 07:39:56 +01:00
SPRINX0\prochazka
d0b4ca33c2 changelog 2026-01-28 07:39:45 +01:00
Jan Prochazka
160391f5a9 SYNC: Merge pull request #43 from dbgate/feature/editor-theme 2026-01-27 15:10:02 +00:00
SPRINX0\prochazka
dfe4a96b02 SYNC: added missing test run 2026-01-27 14:11:15 +00:00
SPRINX0\prochazka
a3f67eb519 SYNC: fixed test 2026-01-27 13:36:07 +00:00
Jan Prochazka
0f9d52552b SYNC: Merge pull request #42 from dbgate/feature/redis-test 2026-01-27 11:59:53 +00:00
SPRINX0\prochazka
a217de4c39 english in templates 2026-01-27 10:47:19 +01:00
SPRINX0\prochazka
d2d85e63f6 english in issue templates 2026-01-27 10:45:51 +01:00
SPRINX0\prochazka
7a6077b5ff SYNC: Ability to skip computed columns is SQL generator SQL Generator #1319 2026-01-26 15:06:56 +00:00
SPRINX0\prochazka
d48c4d9729 SYNC: fixed: The JsonB field in the cell data view always displays as null. #1320 2026-01-26 14:17:27 +00:00
SPRINX0\prochazka
6d677401bf SYNC: FIXED: Foreign key actions not detected on PostgreSQL #1323 2026-01-26 13:56:12 +00:00
SPRINX0\prochazka
a3d4fa2f86 SYNC: fix 2026-01-26 13:04:56 +00:00
SPRINX0\prochazka
59e19b6a22 SYNC: translations 2026-01-26 12:58:25 +00:00
SPRINX0\prochazka
1a76da40d1 SYNC: datagrid translations 2026-01-26 12:58:24 +00:00
SPRINX0\prochazka
cb15ba01f0 SYNC: solarized theme screenshot 2026-01-26 12:20:15 +00:00
SPRINX0\prochazka
78af7f136e SYNC: korean localization 2026-01-26 11:24:02 +00:00
SPRINX0\prochazka
cc6a95b579 SYNC: korean translation 2026-01-26 11:24:00 +00:00
SPRINX0\prochazka
4b3f723bdc SYNC: missing translations 2026-01-26 11:23:58 +00:00
SPRINX0\prochazka
d372e2ff76 SYNC: translations 2026-01-26 11:23:56 +00:00
SPRINX0\prochazka
4201d1cb1e SYNC: translation 2026-01-26 11:09:08 +00:00
SPRINX0\prochazka
afed70ba63 v7.0.1-premium-beta.4 2026-01-26 11:09:06 +01:00
SPRINX0\prochazka
be488346c5 v7.0.1-premium-beta.3 2026-01-26 11:08:04 +01:00
CI workflows
eeeb688439 chore: auto-update github workflows 2026-01-26 10:08:00 +00:00
CI workflows
b84ce77326 Update pro ref 2026-01-26 10:07:42 +00:00
CI workflows
30fca423dc chore: auto-update github workflows 2026-01-26 10:01:44 +00:00
CI workflows
fabbb31572 Update pro ref 2026-01-26 10:01:27 +00:00
SPRINX0\prochazka
ac76ac004e SYNC: admin file - change content 2026-01-26 10:01:17 +00:00
SPRINX0\prochazka
9d2051183a v7.0.1-premium-beta.2 2026-01-26 10:24:19 +01:00
SPRINX0\prochazka
942fdb51d5 v7.0.1-premium.beta.2 2026-01-26 10:20:44 +01:00
CI workflows
d2600a3168 chore: auto-update github workflows 2026-01-26 09:19:22 +00:00
CI workflows
c4248cce22 Update pro ref 2026-01-26 09:19:07 +00:00
SPRINX0\prochazka
16f16f9fed theme docs 2026-01-23 14:51:55 +01:00
SPRINX0\prochazka
d49cb976bc v7.0.1-premium-beta.1 2026-01-23 14:47:45 +01:00
SPRINX0\prochazka
6fae6a9865 changelog 2026-01-23 14:47:23 +01:00
SPRINX0\prochazka
06f3730756 SYNC: upgraded cross-spawn #1322 2026-01-23 13:25:50 +00:00
SPRINX0\prochazka
30e1333f75 SYNC: axios upgrade #1322 2026-01-23 13:18:56 +00:00
Jan Prochazka
0d6fa98767 SYNC: redis test changed 2026-01-22 14:17:52 +00:00
SPRINX0\prochazka
ae6c9edd0d SYNC: green theme screenshot 2026-01-22 12:07:55 +00:00
SPRINX0\prochazka
35de1f1c4e v7.0.0 2026-01-22 10:07:46 +01:00
SPRINX0\prochazka
57142f4afb v7.0.0-alpha.12 2026-01-22 09:47:33 +01:00
CI workflows
cd72d65b89 chore: auto-update github workflows 2026-01-22 08:46:26 +00:00
CI workflows
2199ab0513 Update pro ref 2026-01-22 08:46:10 +00:00
SPRINX0\prochazka
e93f058109 Revert "downgraded NPM refs"
This reverts commit 3f05934b6b.
2026-01-22 09:43:27 +01:00
SPRINX0\prochazka
b68de49cbd v7.0.0-alpha.11 2026-01-22 09:41:58 +01:00
SPRINX0\prochazka
3f05934b6b downgraded NPM refs 2026-01-22 09:41:45 +01:00
SPRINX0\prochazka
3a5713dbb7 v7.0.0-alpha.10 2026-01-22 09:37:36 +01:00
SPRINX0\prochazka
4c43158285 v7.0.0-beta.9 2026-01-22 09:37:24 +01:00
SPRINX0\prochazka
daa743b3b3 upgraded NPM version 2026-01-22 09:37:13 +01:00
SPRINX0\prochazka
41f0ae18c4 changelog - 7.0.0 2026-01-22 09:37:13 +01:00
CI workflows
e6b8aefe5b chore: auto-update github workflows 2026-01-22 07:09:52 +00:00
CI workflows
8b2437cb16 Update pro ref 2026-01-22 07:09:37 +00:00
SPRINX0\prochazka
292495ab0d SYNC: redis CSS variables renamed 2026-01-22 07:09:25 +00:00
CI workflows
017b137d7f chore: auto-update github workflows 2026-01-22 07:02:40 +00:00
CI workflows
7969030313 Update pro ref 2026-01-22 07:02:25 +00:00
SPRINX0\prochazka
c8efad4c3f SYNC: fix 2026-01-22 07:02:13 +00:00
SPRINX0\prochazka
7bf9d8f675 SYNC: redis screenshot 2026-01-22 06:47:32 +00:00
SPRINX0\prochazka
e275f15f00 SYNC: redis hash screenshot 2026-01-21 18:10:20 +00:00
SPRINX0\prochazka
30017a5217 v7.0.0-premium-beta.8 2026-01-21 18:56:13 +01:00
Jan Prochazka
64c5cbe8c3 SYNC: Merge pull request #41 from dbgate/feature/widget-panel 2026-01-21 17:53:24 +00:00
Jan Prochazka
b2b226573c SYNC: Merge pull request #40 from dbgate/feature/redis-refactor-2 2026-01-21 17:44:43 +00:00
CI workflows
69f796998f chore: auto-update github workflows 2026-01-21 10:59:33 +00:00
CI workflows
4d64be3ac7 Update pro ref 2026-01-21 10:59:16 +00:00
Stela Augustinova
4408b794d6 v7.0.0-premium-beta.7 2026-01-21 11:27:06 +01:00
SPRINX0\prochazka
666da8a879 v7.0.0-premium-beta.5 2026-01-21 10:47:10 +01:00
SPRINX0\prochazka
b166342579 v7.0.0-premium-beta.4 2026-01-21 10:47:10 +01:00
SPRINX0\prochazka
433f5bf7d2 compiled workflows 2026-01-21 10:47:10 +01:00
SPRINX0\prochazka
b8ae153ef5 set python version 2026-01-21 10:47:10 +01:00
Jan Prochazka
bb59c2bab7 SYNC: Merge pull request #38 from dbgate/feature/db-icons-light-dark 2026-01-21 08:36:42 +00:00
CI workflows
ab7c6c5118 chore: auto-update github workflows 2026-01-20 09:16:40 +00:00
CI workflows
85c1ea449e Update pro ref 2026-01-20 09:16:26 +00:00
Jan Prochazka
b51d679b78 SYNC: Merge pull request #37 from dbgate/feature/theme-refactor 2026-01-20 09:16:15 +00:00
Jan Prochazka
2a2bc9e625 SYNC: Merge pull request #36 from dbgate/feature/redis-refactor 2026-01-19 15:44:14 +00:00
SPRINX0\prochazka
d00f059567 SYNC: redis styling 2026-01-19 14:27:00 +00:00
SPRINX0\prochazka
81a840347c SYNC: fixed for system dark mode 2026-01-19 14:11:39 +00:00
SPRINX0\prochazka
e691675bf9 SYNC: better mysql icon 2026-01-19 13:43:37 +00:00
SPRINX0\prochazka
9cd57c3ae1 SYNC: removed console.log 2026-01-19 13:31:26 +00:00
CI workflows
0e3310a39b chore: auto-update github workflows 2026-01-19 13:20:59 +00:00
CI workflows
447818ac2a Update pro ref 2026-01-19 13:20:44 +00:00
Jan Prochazka
dd0eb846b0 SYNC: Merge pull request #35 from dbgate/feature/theme-refactor 2026-01-19 13:20:34 +00:00
Jan Prochazka
1b62ca4b21 SYNC: Merge pull request #34 from dbgate/feature/refresh-keys 2026-01-19 13:20:33 +00:00
SPRINX0\prochazka
97ece03853 v7.0.0-premium-beta.3 2026-01-19 12:06:50 +01:00
CI workflows
33a72a0d9d chore: auto-update github workflows 2026-01-18 12:59:19 +00:00
CI workflows
62e61829b4 Update pro ref 2026-01-18 12:59:05 +00:00
Jan Prochazka
ad9d12260e SYNC: Merge pull request #33 from dbgate/feature/theme-refactor 2026-01-18 12:58:54 +00:00
CI workflows
fe54c9495f chore: auto-update github workflows 2026-01-16 17:37:53 +00:00
CI workflows
456afce6ca Update pro ref 2026-01-16 17:37:34 +00:00
Jan Prochazka
8ecbb4d4a4 SYNC: Merge pull request #32 from dbgate/feature/theme-refactor 2026-01-16 17:37:25 +00:00
CI workflows
92564397f4 chore: auto-update github workflows 2026-01-13 18:16:19 +00:00
CI workflows
62b2805c9d Update pro ref 2026-01-13 18:15:53 +00:00
Jan Prochazka
985af8b0bb SYNC: Merge pull request #31 from dbgate/feature/theme-refactor 2026-01-13 18:15:39 +00:00
Jan Prochazka
58c3b180dc SYNC: Merge pull request #30 from dbgate/feature/theme-refactor 2026-01-12 16:07:18 +00:00
CI workflows
d260959b96 chore: auto-update github workflows 2026-01-09 15:48:18 +00:00
CI workflows
6e11197348 Update pro ref 2026-01-09 15:48:04 +00:00
Jan Prochazka
04d5bef4d2 SYNC: Merge pull request #29 from dbgate/feature/theme-colors 2026-01-09 15:47:53 +00:00
SPRINX0\prochazka
1ccc5ce4e1 v7.0.0-beta.2 2026-01-09 13:54:54 +01:00
Stela Augustinova
a94ef1db80 Merge commit '1c493a8dc0ef5d64cfa6044b68107c8245befd98' 2026-01-09 13:53:19 +01:00
Jan Prochazka
5513c624c3 SYNC: Merge pull request #28 from dbgate/feature/redis-wip-1 2026-01-09 12:24:44 +00:00
Stela Augustinova
1c493a8dc0 v7.0.0-premium-beta.1 2026-01-09 09:43:04 +01:00
CI workflows
3fa051a6c3 chore: auto-update github workflows 2026-01-08 13:09:18 +00:00
CI workflows
876171b83d Update pro ref 2026-01-08 13:09:05 +00:00
Jan Prochazka
68521298ff SYNC: Merge pull request #27 from dbgate/feature/team-folders 2026-01-08 13:08:53 +00:00
Jan Prochazka
c648926e49 SYNC: Merge pull request #26 from dbgate/feature/theme-colors 2026-01-08 08:48:38 +00:00
CI workflows
1cd259a261 chore: auto-update github workflows 2026-01-08 07:26:57 +00:00
SPRINX0\prochazka
ef1fe07157 images from master 2026-01-08 08:26:37 +01:00
Jan Prochazka
9ae8f7f406 SYNC: Merge pull request #25 from dbgate/feature/redis-gui-refactor 2026-01-07 12:25:04 +00:00
SPRINX0\prochazka
363cbaac32 SYNC: Pin button for opened tab 2026-01-06 10:08:27 +00:00
SPRINX0\prochazka
435b2cf23c SYNC: opened tabs filter, closed tabs filter 2026-01-06 09:32:24 +00:00
SPRINX0\prochazka
bd1a87f5f0 SYNC: opened tabs widget 2026-01-06 08:30:11 +00:00
SPRINX0\prochazka
c57218adb2 SYNC: removed toolbar 2026-01-06 07:39:09 +00:00
SPRINX0\prochazka
a56d2a3eca SYNC: removed usages of ToolbarButton 2026-01-06 07:28:44 +00:00
SPRINX0\prochazka
b324130e30 SYNC: fixed adding to favorites 2026-01-06 07:08:52 +00:00
SPRINX0\prochazka
566a6e5836 Differentiate pinned database with same name by color mark #1306 2026-01-05 17:07:52 +01:00
SPRINX0\prochazka
bcca407a5e Differentiate pinned database with same name #1306 2026-01-05 16:58:04 +01:00
SPRINX0\prochazka
10c6832718 SYNC: fixed E2E test 2026-01-05 14:55:12 +00:00
SPRINX0\prochazka
1ad8db20b4 SYNC: fixed E2E tests 2026-01-02 15:47:25 +00:00
SPRINX0\prochazka
43233747be widget icon panel theming 2026-01-02 16:03:37 +01:00
SPRINX0\prochazka
56354afa9b removed old theming 2026-01-02 15:55:50 +01:00
SPRINX0\prochazka
7f686a817b fake component 2026-01-02 15:37:30 +01:00
CI workflows
10534ff75a chore: auto-update github workflows 2026-01-02 14:28:03 +00:00
CI workflows
da39be79a4 Update pro ref 2026-01-02 14:27:48 +00:00
SPRINX0\prochazka
8a1ca80e28 removed file 2026-01-02 15:16:04 +01:00
CI workflows
ca29a3a272 chore: auto-update github workflows 2026-01-02 14:12:17 +00:00
CI workflows
1a115bda82 Update pro ref 2026-01-02 14:12:00 +00:00
Jan Prochazka
130e49bf1d SYNC: Merge pull request #24 from dbgate/feature/theme-chat 2026-01-02 14:11:46 +00:00
CI workflows
552c84f910 chore: auto-update github workflows 2025-12-29 15:05:27 +00:00
CI workflows
d1a0111705 Update pro ref 2025-12-29 15:05:12 +00:00
Jan Prochazka
5bc285041b SYNC: Merge pull request #23 from dbgate/feature/db-icons 2025-12-29 15:05:02 +00:00
Jan Prochazka
c96a522a1b SYNC: postgres sfill 2025-12-29 14:21:08 +00:00
Jan Prochazka
def2dadca1 Merge pull request #1307 from dbgate/feature/redis-gui-refactor
Feature/redis gui refactor
2025-12-29 15:18:32 +01:00
Jan Prochazka
46a52e2b2f Merge branch 'stable' 2025-12-29 14:12:20 +01:00
Jan Prochazka
1835776200 changelog 2025-12-29 14:11:52 +01:00
Jan Prochazka
efaa4893bf v6.8.2 2025-12-29 14:08:42 +01:00
Jan Prochazka
be8580cc4b v6.8.2-premium-beta.1 2025-12-29 11:20:21 +01:00
Jan Prochazka
e43bb3123b fixed connections scaffold for postgres 2025-12-29 11:19:58 +01:00
Stela Augustinova
3078e3584c Refactor DbKeyDetailTab to streamline insert handling for Redis data types 2025-12-23 19:32:50 +01:00
Stela Augustinova
6689849f97 Add field insert functionality for Redis data types 2025-12-23 19:04:41 +01:00
Stela Augustinova
7281ee1565 Refactor DbKeyDetailTab and related components to handle item changes 2025-12-23 14:22:31 +01:00
Stela Augustinova
4c12ee3b14 Added delete functionality for list, hash, set, zset and stream fields. 2025-12-23 09:05:04 +01:00
SPRINX0\prochazka
dc0ae69f65 Merge branch 'stable' 2025-12-22 11:52:42 +01:00
SPRINX0\prochazka
0dba4ba653 changelog 2025-12-22 11:52:23 +01:00
SPRINX0\prochazka
758d8689ab v6.8.1 2025-12-22 11:51:25 +01:00
Jan Prochazka
f119ccf5a0 SYNC: Merge pull request #22 from dbgate/feature/tailwind-poc 2025-12-22 08:53:36 +00:00
CI workflows
c86c6486b9 chore: auto-update github workflows 2025-12-22 07:27:54 +00:00
SPRINX0\prochazka
6ed5c163ba Merge branch 'stable' 2025-12-22 08:27:29 +01:00
SPRINX0\prochazka
2c14530e3c fixded data grid column click scroll #1303 2025-12-22 08:20:23 +01:00
Jan Prochazka
6d30e1921e Merge pull request #1302 from dbgate/feature/db-icons
Feature/db icons
2025-12-19 15:45:35 +01:00
Stela Augustinova
7ff84a9932 Refactor DbKeyDetailTab to integrate Redis data type editors for list, hash, zset, set, and stream 2025-12-19 15:40:09 +01:00
Stela Augustinova
bcff01b0bf Implement handleSaveRedisData for Redis data types including JSON, hash, zset, and list 2025-12-19 15:16:19 +01:00
Stela Augustinova
8cd09342e1 Add read-only attribute to Redis driver key fields 2025-12-19 15:10:30 +01:00
Jan Prochazka
802e78d3b7 Merge pull request #1190 from dbgate/feature/svelte4
Feature/svelte4
2025-12-19 14:00:23 +01:00
Stela Augustinova
e6c938e5d0 Implement saveRedisData functionality and update API call in DbKeyDetailTab 2025-12-19 13:46:09 +01:00
Stela Augustinova
7b6595124f Refactor DbKeyDetailTab to replace DbKeyItemEdit with DbKeyItemDetail and streamline change tracking logic 2025-12-19 13:45:28 +01:00
SPRINX0\prochazka
c33408a1f7 yarn lock 2025-12-19 13:29:59 +01:00
SPRINX0\prochazka
160d53701b Merge branch 'master' into feature/svelte4 2025-12-19 13:26:16 +01:00
SPRINX0\prochazka
1f19d1925a screenshots only from stable branch 2025-12-19 13:23:02 +01:00
Stela Augustinova
50680a4f2e Added read-only field handling for Redis data types 2025-12-19 12:27:10 +01:00
Stela Augustinova
18307b2e03 Refactor DbKeyItemDetail and DbKeyValueDetail for improved layout and editor responsiveness 2025-12-19 12:19:07 +01:00
Stela Augustinova
b03ad80451 Update icons for various database drivers and improve FontIcon styles 2025-12-19 09:34:44 +01:00
SPRINX0\prochazka
dedfe1f421 SYNC: filter by expanded column 2025-12-18 15:57:53 +00:00
SPRINX0\prochazka
1cf583d197 SYNC: fix 2025-12-18 15:34:37 +00:00
Stela Augustinova
1deaa4c30d Refactor DbKeyTab to replace FormStyledButton with ToolStripButton 2025-12-18 16:30:01 +01:00
Stela Augustinova
90316f106a Fixed adding zset key 2025-12-18 16:26:32 +01:00
SPRINX0\prochazka
63693f908d SYNC: group by screenshot 2025-12-18 15:12:29 +00:00
Stela Augustinova
6905e4a2a1 Refactor DbKeyTab to integrate new DbKeyValue components for improved Redis data type handling 2025-12-18 16:05:48 +01:00
CI workflows
4489a54e82 chore: auto-update github workflows 2025-12-18 14:17:05 +00:00
CI workflows
6d48915945 Update pro ref 2025-12-18 14:16:46 +00:00
SPRINX0\prochazka
00f3a7f4db SYNC: translations 2025-12-18 11:48:40 +00:00
Stela Augustinova
f9c47ab233 Add DbKeyValueHashEdit component for editing hash key values in DbKeyTab 2025-12-18 12:37:12 +01:00
Stela Augustinova
28712b205f Refactor DbKeyDetailTab to use DbKeyItemEdit component for editing key items 2025-12-18 09:15:56 +01:00
Stela Augustinova
69b1fb1bfd Add support for SVG icons in FontIcon component and update connection icon logic 2025-12-17 16:15:35 +01:00
Stela Augustinova
8a22f9215f Added svg icons for engines 2025-12-17 16:13:20 +01:00
SPRINX0\prochazka
c5ebc01978 v6.8.0 2025-12-17 15:38:39 +01:00
SPRINX0\prochazka
f29b468fc1 changelog 2025-12-17 15:33:41 +01:00
SPRINX0\prochazka
e796fbb990 save export jobs only in proapp 2025-12-17 14:58:12 +01:00
CI workflows
37a122c981 chore: auto-update github workflows 2025-12-17 13:42:41 +00:00
CI workflows
4f426a73f6 Update pro ref 2025-12-17 13:42:25 +00:00
Jan Prochazka
1bcb74cd85 SYNC: Merge pull request #20 from dbgate/feature/sfill2 2025-12-17 13:42:13 +00:00
SPRINX0\prochazka
cd88c8de78 v6.7.4-premium-beta.2 2025-12-17 12:28:27 +01:00
CI workflows
eee288b45b chore: auto-update github workflows 2025-12-17 11:27:35 +00:00
CI workflows
797cb7615d Update pro ref 2025-12-17 11:27:20 +00:00
Jan Prochazka
ca4667ff1e SYNC: Merge pull request #19 from dbgate/feature/sfill 2025-12-17 11:27:08 +00:00
Jan Prochazka
66d1143ca0 replicator - support for skip update columns 2025-12-16 14:43:44 +01:00
SPRINX0\prochazka
f310916c76 SYNC: fix 2025-12-16 09:30:38 +00:00
SPRINX0\prochazka
5d3d8ab932 v6.7.4-beta.1 2025-12-16 09:02:17 +01:00
Stela Augustinova
07117c90d1 Use changeset in DbKeyTableControl, update values 2025-12-15 16:19:00 +01:00
SPRINX0\prochazka
fd91c18460 SYNC: fixed e2e tests + new form cell view test 2025-12-15 15:03:43 +00:00
Stela Augustinova
5d92c0e85e Refactor ChangeSetRedis_Hash interface to use 'key' instead of 'field' for inserts and updates 2025-12-15 14:29:45 +01:00
Jan Prochazka
2a12c04518 Merge pull request #1299 from dbgate/feature/wordwrap-editor
Feature/wordwrap editor
2025-12-15 13:26:54 +01:00
CI workflows
d08cae6fa3 chore: auto-update github workflows 2025-12-15 12:23:33 +00:00
SPRINX0\prochazka
d7f9de1881 concurrency settings for workflows 2025-12-15 13:23:10 +01:00
SPRINX0\prochazka
962190cc57 SYNC: code cleanup 2025-12-15 12:08:07 +00:00
SPRINX0\prochazka
4527866276 SYNC: form cell view - show JSON 2025-12-15 12:08:05 +00:00
SPRINX0\prochazka
088dfcd4dc SYNC: renamed table cell view => form cell view 2025-12-15 12:08:03 +00:00
SPRINX0\prochazka
6c317b6e64 SYNC: table cell view - single click 2025-12-15 12:08:01 +00:00
SPRINX0\prochazka
6b66c273b4 SYNC: fixed display numbers 2025-12-15 12:07:59 +00:00
SPRINX0\prochazka
60f31008c0 SYNC: table cell view UX 2025-12-15 12:07:58 +00:00
SPRINX0\prochazka
078f74db97 SYNC: cell data - allow to edit 2025-12-15 12:07:56 +00:00
SPRINX0\prochazka
a0b025cf59 SYNC: Run macro context menu 2025-12-15 12:07:54 +00:00
SPRINX0\prochazka
bc695f5af9 SYNC: run macro WIP 2025-12-15 12:07:52 +00:00
SPRINX0\prochazka
9685e63b09 SYNC: table cell view refactor 2025-12-15 12:07:50 +00:00
SPRINX0\prochazka
142791360c SYNC: working widget resizing 2025-12-15 12:07:48 +00:00
SPRINX0\prochazka
e004ed2f4b SYNC: widget bar fix 2025-12-15 12:07:47 +00:00
SPRINX0\prochazka
23ed487252 SYNC: fix 2025-12-15 12:07:45 +00:00
SPRINX0\prochazka
efefec3c20 SYNC: widgetbar refactor 2025-12-15 12:07:43 +00:00
SPRINX0\prochazka
3d2ad1cb9b SYNC: resize WIP 2025-12-15 12:07:41 +00:00
SPRINX0\prochazka
90d3016938 SYNC: resize heights 2025-12-15 12:07:39 +00:00
SPRINX0\prochazka
438f9fc94d SYNC: better widget panel height processing 2025-12-15 12:07:37 +00:00
SPRINX0\prochazka
82ec88cc2f SYNC: recompute WIP 2025-12-15 12:07:36 +00:00
SPRINX0\prochazka
149611041e SYNC: widget configuration saved to storage 2025-12-15 12:07:33 +00:00
SPRINX0\prochazka
b12c79462e SYNC: widget column bar update 2025-12-15 12:07:31 +00:00
SPRINX0\prochazka
fbf34fb730 SYNC: widgetcolumnbar refactor 2025-12-15 12:07:29 +00:00
SPRINX0\prochazka
e1fe3eb710 SYNC: widgetcolumnbar props 2025-12-15 12:07:27 +00:00
SPRINX0\prochazka
76ae2e0e5a SYNC: improved data grid navigation 2025-12-15 12:07:25 +00:00
SPRINX0\prochazka
a57063adf7 SYNC: refactor 2025-12-15 12:07:24 +00:00
SPRINX0\prochazka
ff0157e624 SYNC: autodetect data grid cell 2025-12-15 12:07:22 +00:00
SPRINX0\prochazka
af9701feb8 SYNC: cell data view 2025-12-15 12:07:20 +00:00
SPRINX0\prochazka
93c1f31588 SYNC: removed selectedCellsCallback 2025-12-15 12:07:17 +00:00
SPRINX0\prochazka
1964e54476 SYNC: cell data widget moved 2025-12-15 12:07:15 +00:00
Stela Augustinova
4682255d5f Refactor SQL editor settings layout and update word wrap field type 2025-12-15 13:02:11 +01:00
Stela Augustinova
a503898b21 Refactor SQL editor to integrate word wrap settings and remove redundant options in QueryTab 2025-12-15 12:44:04 +01:00
Stela Augustinova
21352dae07 Revert "Implement word wrap feature in SQL editor and settings"
This reverts commit 28aa86f0aa.
2025-12-15 12:37:15 +01:00
Jan Prochazka
8470c7ac6b Merge pull request #1297 from dbgate/feature/redis-number-of-db
Retrieve the number of databases from Redis configuration
2025-12-15 10:50:53 +01:00
Stela Augustinova
28aa86f0aa Implement word wrap feature in SQL editor and settings 2025-12-14 17:26:25 +01:00
Stela Augustinova
3ed214269a Retrieve the number of databases from Redis configuration 2025-12-12 11:15:26 +01:00
Stela Augustinova
37b5183be2 Add ChangeSetRedis interfaces for Redis data types 2025-12-11 15:02:32 +01:00
Stela Augustinova
a79896aa2e Added TTL for hash fields 2025-12-10 16:46:13 +01:00
Stela Augustinova
d5bd40873e Update icon class for ReJSON type in FontIcon component 2025-12-10 15:55:45 +01:00
Stela Augustinova
52f1809d22 Pass keyType to DbKeyValueDetail for AceEditor mode 2025-12-10 15:55:24 +01:00
Stela Augustinova
51d8fa7268 Add support for JSON type in getIconForRedisType function 2025-12-10 15:44:48 +01:00
Stela Augustinova
4d1167a6d6 Fix parsing of JSON data 2025-12-10 15:44:36 +01:00
Stela Augustinova
8fa1459e5b Add support for ReJSON-RL commands and JSON data type in Redis driver 2025-12-10 14:32:36 +01:00
Stela Augustinova
baf3914be8 Updated button label from 'Add item' to 'Add field' 2025-12-10 13:57:40 +01:00
Stela Augustinova
bd2721d3ec Added key rename modal 2025-12-10 13:57:09 +01:00
Stela Augustinova
f30b96b360 Refactor DbKeyDetailTab to use ToolStrip for action buttons and improve layout structure 2025-12-10 12:34:28 +01:00
Stela Augustinova
4772c0e110 Add support for 'zadd' command and update key fields in Sorted Set configuration 2025-12-10 12:18:45 +01:00
Stela Augustinova
4a0af08ae5 Update key type display in DbKeyDetailTab to use label if available 2025-12-10 10:34:19 +01:00
SPRINX0\prochazka
a71129df4b SYNC: AI assistant 2025-12-10 07:13:16 +00:00
SPRINX0\prochazka
de6acfa1ce Revert "Revert "MPR archive""
This reverts commit ccf075dc65.
2025-12-10 07:48:30 +01:00
SPRINX0\prochazka
ccf075dc65 Revert "MPR archive"
This reverts commit 391d04b45c.
2025-12-10 07:36:03 +01:00
SPRINX0\prochazka
1d8ac3cf86 Revert "MPR advanced exports"
This reverts commit 864797fc99.
2025-12-10 07:36:03 +01:00
Stela Augustinova
623a23492f Add initial key name support in DbKeyTab and DbKeysTree components 2025-12-09 16:39:36 +01:00
Jan Prochazka
7a8ff89c5c Merge pull request #1293 from dbgate/feature/FK-test
Feature/fk test
2025-12-09 16:22:29 +01:00
CI workflows
eda70def2a chore: auto-update github workflows 2025-12-09 15:02:50 +00:00
CI workflows
08fd75edc7 Update pro ref 2025-12-09 15:02:30 +00:00
Jan Prochazka
15ea53864f SYNC: Merge pull request #18 from dbgate/feature/translation5 2025-12-09 15:02:19 +00:00
Stela Augustinova
056ee0d58e Created tab for adding key, replaced modal 2025-12-09 13:55:21 +01:00
Jan Prochazka
377cd64556 Revert "try to comment out earlier patch"
This reverts commit 955ca99cf3.
2025-12-09 12:56:47 +01:00
Stela Augustinova
b37744d574 Merge pull request #1296 from dbgate/feature/map-autodetect-lat-lon
Feature/map autodetect lat lon
2025-12-09 11:01:49 +01:00
Stela Augustinova
a7f21fe0c6 Merge pull request #1292 from dbgate/feature/table-cell-data-view
Feature/table cell data view
2025-12-09 11:00:19 +01:00
Jan Prochazka
955ca99cf3 try to comment out earlier patch 2025-12-09 10:46:25 +01:00
Jan Prochazka
98f5bb4124 sanitize constraints 2025-12-09 10:45:38 +01:00
Jan Prochazka
b3943f005d alter table fixed 2025-12-08 17:35:29 +01:00
Jan Prochazka
8d4178b984 grouped table recreates 2025-12-08 16:57:09 +01:00
Stela Augustinova
2a88ed38c4 Added translation tags to TableCellView component, updated decimal handling 2025-12-08 16:45:18 +01:00
Jan Prochazka
52dce7dfd3 disabled grouping recreate table OPs 2025-12-08 16:41:55 +01:00
Stela Augustinova
6ebee92542 Merge branch 'master' into feature/table-cell-data-view 2025-12-08 16:07:55 +01:00
Jan Prochazka
1b5646f526 Revert "fix: correct reference from wholeNewDb to wholeOldDb in AlterPlan class"
This reverts commit 12e6afbaad.
2025-12-08 16:05:50 +01:00
Jan Prochazka
7024e4b40d Merge pull request #1289 from dbgate/feature/postgresql-decimal
Postgresql decimal
2025-12-08 15:42:19 +01:00
Jan Prochazka
bc2e27d7da Merge pull request #1290 from david-pivonka/feature/table-cell-data-view
Add Table format to Cell data view sidebar
2025-12-08 15:35:35 +01:00
Jan Prochazka
189da2bfe2 Merge pull request #1291 from david-pivonka/fix/map-autodetect-lat-lon
Improve Map view lat/lon field autodetection
2025-12-08 15:33:58 +01:00
Stela Augustinova
12e6afbaad fix: correct reference from wholeNewDb to wholeOldDb in AlterPlan class 2025-12-08 15:27:45 +01:00
David Pivoňka
142ebe3d27 Fix scrolling in Table - Row view
Use absolute positioning pattern for proper scrolling behavior
when many columns are displayed.
2025-12-08 15:23:59 +01:00
Stela Augustinova
7579f6e42a fix: comment out incremental analysis in testTableDiff and correct clickhouse image reference 2025-12-08 15:19:27 +01:00
David Pivoňka
38c25cae74 Add multi-row selection support with bulk editing
- Show "(Multiple values)" when selected rows have different values
- Allow bulk editing: changes apply to all selected rows
- Rename format to "Table - Row" for clarity
2025-12-08 15:01:02 +01:00
David Pivoňka
408496eb7c Improve Map view lat/lon field autodetection
Prioritize field names that are more likely to be actual latitude/longitude
fields instead of randomly selecting the first numeric field containing
"lat" or "lon" in its name.
2025-12-08 14:42:56 +01:00
Jan Prochazka
4d61c74a8b fixed tes on CI 2025-12-08 14:34:45 +01:00
David Pivoňka
190c610466 Add column filter/search to Table cell data view
Adds a search input at the top of the Table view that filters columns
by name with regex support.
2025-12-08 14:31:38 +01:00
Jan Prochazka
85b7e3ebe3 fixed sqlite test folder 2025-12-08 14:18:28 +01:00
Stela Augustinova
c6d3fc06a3 Add support for deserializing decimal type 2025-12-08 13:57:10 +01:00
David Pivoňka
d220525ac7 Use braces for isChangedRef.get() blocks to match codebase style 2025-12-08 13:47:35 +01:00
David Pivoňka
5e4a631ff2 Remove comments and apply early return pattern 2025-12-08 13:43:41 +01:00
David Pivoňka
9099ce42b9 Add Table format to Cell data view sidebar
Adds a new "Table" format option to the Cell data view widget that
displays the selected row as a vertical list with column names above
values, inspired by TablePlus.

Features:
- Shows all columns from the selected row in grid display order
- Inline editing support for regular values (double-click to edit)
- JSON values open Edit Cell modal on double-click
- Open-in-new button for JSON values to view in JSON tab
2025-12-08 13:37:55 +01:00
Jan Prochazka
df226fea22 import test - greater timeout 2025-12-08 13:12:08 +01:00
Jan Prochazka
851d2e9151 fixed double drop constraint 2025-12-08 13:05:38 +01:00
Stela Augustinova
e67ee4ffdb Add support for PostgreSQL decimal type in filter and grid utilities 2025-12-08 12:52:47 +01:00
Stela Augustinova
2baf975847 Added PostgreSQL decimal type in DataGridCell component 2025-12-05 13:14:14 +01:00
Stela Augustinova
c1672ebc8e Handling decimal values in putValue method 2025-12-05 13:13:56 +01:00
Stela Augustinova
bbbd291065 Formatting decimal values in stringifyCellValue function 2025-12-05 13:13:29 +01:00
Stela Augustinova
0a3c1efdd4 Add support for parsing bigint and decimal types in PostgreSQL driver 2025-12-05 13:13:08 +01:00
SPRINX0\prochazka
89121a2608 handled UTF-8 BOM in CSV input 2025-12-04 16:44:08 +01:00
SPRINX0\prochazka
23cf264d4d fix 2025-12-04 16:29:06 +01:00
Stela Augustinova
b3130225b5 Filter out primary key columns in nullability change tests 2025-12-04 14:53:08 +01:00
Stela Augustinova
65512defed Merge branch 'master' into feature/FK-test 2025-12-04 14:36:14 +01:00
Stela Augustinova
3b1c8748f1 Add createForeignKeyFore method to handle foreign key creation (Oracle) 2025-12-04 14:34:26 +01:00
Stela Augustinova
aba660eddb Fix nullability filter in alter table tests 2025-12-04 14:08:22 +01:00
Stela Augustinova
137eac7dbf Add dropColumnDependencies property to dialect configuration 2025-12-04 14:07:55 +01:00
Stela Augustinova
fdbd08f511 Added FK constraint type for sqlite 2025-12-04 13:00:06 +01:00
Stela Augustinova
ace1cec1f6 Delete FK from drop column 2025-12-04 10:00:48 +01:00
Jan Prochazka
0c15e524d7 changelog 2025-12-03 18:32:14 +01:00
Jan Prochazka
b0b5b1c30d v6.7.3 2025-12-03 18:22:02 +01:00
Jan Prochazka
30b4c85c5a better formating 2025-12-03 18:21:04 +01:00
Jan Prochazka
910f9cadfe v6.7.3-premium-beta.1 2025-12-03 17:37:20 +01:00
Jan Prochazka
6de37ebd16 cypress mocha reporter 2025-12-03 17:33:37 +01:00
Jan Prochazka
de57c4e87e Skip tests with AI API calls 2025-12-03 17:14:51 +01:00
Jan Prochazka
b85cf66490 Merge branch 'feature/pgsql-droptable-fix' 2025-12-03 17:06:45 +01:00
Jan Prochazka
8e638ea9a6 fix 2025-12-03 17:00:04 +01:00
Jan Prochazka
b4849ec495 fix problem with diff analysing after drop object 2025-12-03 16:47:50 +01:00
Jan Prochazka
09c12d52ac more tests 2025-12-03 16:39:42 +01:00
Jan Prochazka
db6a2ddd7e Merge pull request #1286 from dbgate/feature/custom-thousands-separator
Feature/custom thousands separator
2025-12-03 15:40:04 +01:00
Jan Prochazka
12ef9463ab Merge pull request #1284 from dbgate/feature/numeric-align-right
Added isTypeNumber check for right alignment in DataGridCell
2025-12-03 15:37:58 +01:00
Stela Augustinova
fa5fda0c3b Incremental analysis 2025-12-03 15:31:05 +01:00
Stela Augustinova
251609e274 Update foreign key references when dropping or renaming columns in alter table tests 2025-12-03 15:21:12 +01:00
Jan Prochazka
a557ad177e changelog 2025-12-03 14:57:11 +01:00
Stela Augustinova
c0287e49d8 FK test 2025-12-03 14:28:58 +01:00
Stela Augustinova
78e838f2f0 Custom thousands separator formatting in grid cell values 2025-12-03 13:53:47 +01:00
Stela Augustinova
c1f216c7c7 Deleted checkbox for thousands separator and updated select field options 2025-12-03 13:53:10 +01:00
Jan Prochazka
b75ff99e4c v6.7.2 2025-12-03 13:44:35 +01:00
Jan Prochazka
780dd8ade9 language icon 2025-12-03 13:37:13 +01:00
Jan Prochazka
e1c10b7653 v6.7.2-beta.7 2025-12-03 12:57:52 +01:00
Jan Prochazka
be9505f8fe SYNC: translations 2025-12-03 11:55:53 +00:00
Jan Prochazka
d6bcd4f94f changelog 2025-12-03 12:52:40 +01:00
Jan Prochazka
7d2196f4c3 v6.7.2-premium-beta.6 2025-12-03 12:42:25 +01:00
Jan Prochazka
0539174317 SYNC: fixed e2e test 2025-12-03 11:34:55 +00:00
Jan Prochazka
b4b52e12d5 SYNC: try to fix test 2025-12-03 10:13:16 +00:00
CI workflows
f2e0b1cfa2 chore: auto-update github workflows 2025-12-03 10:10:08 +00:00
CI workflows
8020e2a263 Update pro ref 2025-12-03 10:09:55 +00:00
Jan Prochazka
6112d9b1b0 SYNC: settings storage changed 2025-12-03 10:09:42 +00:00
Stela Augustinova
4a1fbcbd31 Added select field for thousands separator 2025-12-03 10:46:34 +01:00
Stela Augustinova
a02a3230f1 Added isTypeNumber check for right alignment in DataGridCell 2025-12-03 10:34:09 +01:00
CI workflows
0218bb4990 chore: auto-update github workflows 2025-12-03 07:43:56 +00:00
CI workflows
3769c03565 Update pro ref 2025-12-03 07:43:40 +00:00
SPRINX0\prochazka
d96cb10476 behaviour settings changed 2025-12-02 18:14:55 +01:00
SPRINX0\prochazka
b6b6123434 refresh DB - don't offer incremental analysis when not supported 2025-12-02 18:07:28 +01:00
SPRINX0\prochazka
b40877fcc1 fix - don't show update mode in web 2025-12-02 17:59:41 +01:00
SPRINX0\prochazka
af5ae29b73 changelog 2025-12-02 17:51:44 +01:00
SPRINX0\prochazka
082fceebbe v6.7.2-premium-beta.5 2025-12-02 17:29:27 +01:00
CI workflows
f1dab80a06 chore: auto-update github workflows 2025-12-02 15:10:33 +00:00
CI workflows
cbf2fac2cf Update pro ref 2025-12-02 15:10:16 +00:00
Jan Prochazka
4564bd7180 SYNC: Merge pull request #17 from dbgate/feature/settings-test 2025-12-02 15:10:00 +00:00
SPRINX0\prochazka
d12ad7b882 SYNC: sorted translation keys 2025-12-02 13:42:48 +00:00
SPRINX0\prochazka
f8e9f07a00 SYNC: translated files 2025-12-02 13:42:46 +00:00
Jan Prochazka
4ac3891e07 Merge pull request #1281 from dbgate/feature/translation4
Feature/translation4
2025-12-02 14:11:51 +01:00
SPRINX0\prochazka
a2d55d3fdd SYNC: removed button 2025-12-02 13:09:32 +00:00
Stela Augustinova
d015a24300 Merge branch 'master' into feature/translation4 2025-12-02 14:03:59 +01:00
Stela Augustinova
be38acbede Update translation tag for columns count 2025-12-02 14:01:04 +01:00
Stela Augustinova
34d0cb4dc7 Refactor translation tags in ResultTabs component to use _t function 2025-12-02 13:58:31 +01:00
SPRINX0\prochazka
18d0558b19 SYNC: fixed language test 2025-12-02 12:47:33 +00:00
SPRINX0\prochazka
d4469f3a2d SYNC: fixed import CSV test 2025-12-02 12:08:16 +00:00
SPRINX0\prochazka
43b760b4bf fix 2025-12-02 12:34:57 +01:00
SPRINX0\prochazka
3d47932c09 v6.7.2-premium-beta.4 2025-12-02 12:26:52 +01:00
SPRINX0\prochazka
7196d6e1bf SYNC: fixed plugin tyab test, moived to add-connection tests 2025-12-02 11:16:05 +00:00
SPRINX0\prochazka
ec06a7d861 SYNC: restore table fixes & backup table tests 2025-12-02 10:08:38 +00:00
Jan Prochazka
ef23f0d18e Merge pull request #1280 from dbgate/feature/settings-tab-update2
Feature/settings tab update2
2025-12-02 10:28:21 +01:00
Stela Augustinova
e9abc5f07f Changed translation tag 2025-12-02 10:27:21 +01:00
Stela Augustinova
6f9d6ff849 Changed initial width of settings menu items 2025-12-02 10:21:39 +01:00
Stela Augustinova
eab18d3c11 Update general settings layout 2025-12-02 10:16:16 +01:00
SPRINX0\prochazka
3a509a6a97 SYNC: backup test WIP 2025-12-01 16:09:20 +00:00
SPRINX0\prochazka
615c6f4e24 SYNC: popup menu fix 2025-12-01 15:43:24 +00:00
CI workflows
96660b4539 chore: auto-update github workflows 2025-12-01 15:37:27 +00:00
CI workflows
1be284974b Update pro ref 2025-12-01 15:37:11 +00:00
Jan Prochazka
790b6478dc Merge pull request #1279 from dbgate/feature/settings-tab-update
Feature/settings tab update
2025-12-01 16:28:21 +01:00
Stela Augustinova
106344b33e Deleted Settings Modal 2025-12-01 16:26:20 +01:00
Stela Augustinova
85a2a4b873 Revert "Button for svg export"
This reverts commit 352b6cbe04.
2025-12-01 16:22:35 +01:00
Stela Augustinova
4ab694de0c Added top border for first menu item 2025-12-01 16:14:23 +01:00
Stela Augustinova
5e193c1725 Removed Other settings component 2025-12-01 16:10:30 +01:00
Stela Augustinova
2b055c028c Update theme command to open settings tab with selected item 2025-12-01 16:00:05 +01:00
SPRINX0\prochazka
b341749e45 v6.7.2-premium-beta.3 2025-12-01 15:48:06 +01:00
Stela Augustinova
6ae381b1fd Merge branch 'master' of https://github.com/dbgate/dbgate 2025-12-01 15:45:42 +01:00
Stela Augustinova
352b6cbe04 Button for svg export 2025-12-01 15:45:16 +01:00
CI workflows
e5e6d2701e chore: auto-update github workflows 2025-12-01 14:28:22 +00:00
CI workflows
9ad1924488 Update pro ref 2025-12-01 14:28:07 +00:00
SPRINX0\prochazka
2aadbfc64a v6.7.2-premium-beta.2 2025-12-01 14:51:22 +01:00
Jan Prochazka
1c5d652f93 SYNC: fixed test 2025-12-01 13:34:34 +00:00
SPRINX0\prochazka
b2355a3b2d Merge branch 'feature/restore-script' 2025-12-01 14:27:29 +01:00
Stela Augustinova
4ee6d089d5 Added translation tags for objects, widgets, forms 2025-12-01 12:54:42 +01:00
SPRINX0\prochazka
6bd81cbff5 restore table - comments 2025-12-01 12:17:00 +01:00
SPRINX0\prochazka
b912190c5e delete part of restore script 2025-12-01 12:07:44 +01:00
Jan Prochazka
43a826e2e5 Merge pull request #1277 from dbgate/feature/handle-full-refresh
Update refresh button to handle full database refresh
2025-12-01 11:07:18 +01:00
SPRINX0\prochazka
8ae64a9dcf restore script - update 2025-12-01 11:06:18 +01:00
Stela Augustinova
4ce9faf39b Update refresh button to handle full database refresh 2025-12-01 10:42:49 +01:00
Stela Augustinova
d94a67d0af Added translation tags for modals 2025-12-01 10:25:43 +01:00
SPRINX0\prochazka
d650d91d82 table restore script WIP 2025-12-01 10:08:00 +01:00
Stela Augustinova
e14f59256d Added translation tags for settings, tabs, modals 2025-11-30 19:38:01 +01:00
SPRINX0\prochazka
d3322a4a15 Merge branch 'feature/table-backups' 2025-11-28 16:10:18 +01:00
SPRINX0\prochazka
a65842e31f table backups 2025-11-28 16:07:36 +01:00
SPRINX0\prochazka
74fde66b51 lang texts 2025-11-28 15:37:34 +01:00
SPRINX0\prochazka
c3ea155a7b refresh database options 2025-11-28 15:35:38 +01:00
Jan Prochazka
c4b81e3d2c Merge pull request #1275 from dbgate/feature/settings-tab
Feature/settings tab
2025-11-28 13:59:41 +01:00
Stela Augustinova
6f6ed1a741 Remove unused tip styles from various settings components 2025-11-28 13:26:45 +01:00
Stela Augustinova
311680c090 Add commands for viewing application logs and managing plugins; update settings tab integration 2025-11-28 13:23:02 +01:00
SPRINX0\prochazka
5e54aa553a skip incremental analysis test for postgres 2025-11-28 12:47:59 +01:00
SPRINX0\prochazka
6913970830 postgresql primary key loading optimalization 2025-11-28 12:32:18 +01:00
Jan Prochazka
014e453e57 removed ttable incremental analysis for postgres 2025-11-28 12:13:32 +01:00
Jan Prochazka
25b5341f76 fix tests 2025-11-28 11:54:24 +01:00
SPRINX0\prochazka
1df51f9609 skip incremental analysis check 2025-11-28 11:01:22 +01:00
Jan Prochazka
65d13189b3 postgres analyser fixed - broken loading FKs in incremental analysis 2025-11-28 10:24:53 +01:00
Jan Prochazka
0913011120 test fix 2025-11-28 08:21:04 +01:00
Stela Augustinova
30ddc18eb1 Add ai settings component to SettingsTab 2025-11-27 16:35:45 +01:00
Jan Prochazka
697d755744 test fix 2025-11-27 16:25:24 +01:00
Jan Prochazka
e3f23ddc79 fixed test 2025-11-27 15:44:31 +01:00
Jan Prochazka
094acc40e8 jest reporters 2025-11-27 15:13:28 +01:00
Jan Prochazka
ebd4991de8 Revert "jest reporters"
This reverts commit d04a8fad4c.
2025-11-27 15:12:15 +01:00
Jan Prochazka
d04a8fad4c jest reporters 2025-11-27 14:55:40 +01:00
CI workflows
cb14bffc5a chore: auto-update github workflows 2025-11-27 13:48:04 +00:00
Jan Prochazka
a4c4d17381 test reporters 2025-11-27 14:47:43 +01:00
Stela Augustinova
21eb27f6e3 Added new settings components, added wrapper to all setting components 2025-11-27 14:42:24 +01:00
Jan Prochazka
abf0fc7942 incremental analysis in alter table tests 2025-11-27 14:06:48 +01:00
SPRINX0\prochazka
c2703edfde prettier 2025-11-27 11:03:12 +01:00
SPRINX0\prochazka
d14b90ab20 foreign key editor UX 2025-11-27 10:29:33 +01:00
SPRINX0\prochazka
48f4924932 prettier format 2025-11-27 09:29:39 +01:00
Stela Augustinova
765551988a Added keyboard shortcuts to settings tab 2025-11-27 09:23:36 +01:00
Stela Augustinova
4883eb0d1b Merge branch 'master' into feature/settings-tab 2025-11-27 09:12:34 +01:00
Stela Augustinova
06a9a93d1c Added translation tag for settings 2025-11-27 09:11:33 +01:00
SPRINX0\prochazka
c92a0e1d43 fixed search in keyboard shortcuts #1273 2025-11-27 08:58:11 +01:00
Stela Augustinova
c6b5ee164b Added settings tab and settings components 2025-11-26 16:21:07 +01:00
SPRINX0\prochazka
c65075f887 v6.7.2-alpha.1 2025-11-26 14:43:54 +01:00
SPRINX0\prochazka
a1f678a3a1 additional connnection options for dbmodel 2025-11-26 14:43:18 +01:00
Jan Prochazka
fe3fefaa4e Merge pull request #1272 from dbgate/feature/translation4
Feature/translation4
2025-11-26 14:12:14 +01:00
Jan Prochazka
3ccebcb0d1 Merge pull request #1271 from dbgate/feature/translation-script
Feature/translation script
2025-11-26 14:10:01 +01:00
Jan Prochazka
dbbae0eef2 Merge pull request #1270 from dbgate/feature/columns-filters-panel-visibility
Feature/columns filters panel visibility
2025-11-26 09:54:50 +01:00
Stela Augustinova
f11c4881f3 Update gitinore 2025-11-25 16:08:36 +01:00
Stela Augustinova
6398c6d7ce Added new translations 2025-11-25 16:03:55 +01:00
Stela Augustinova
973ce8c3a7 Load OpenAI API key 2025-11-25 16:02:44 +01:00
Stela Augustinova
d971869283 Add collapsed left column store for SQL editor settings 2025-11-25 15:17:16 +01:00
SPRINX0\prochazka
987002b8f3 v6.7.1 2025-11-25 14:58:33 +01:00
SPRINX0\prochazka
f73fe495a5 changelog 2025-11-25 14:57:56 +01:00
Stela Augustinova
fe7b0e2bc7 Add translation script using OpenAI for missing keys (not finished) 2025-11-25 14:38:23 +01:00
Stela Augustinova
23937c54e0 Added checkbox to hide columns/filters panel by default 2025-11-25 13:37:19 +01:00
SPRINX0\prochazka
b3d0fd9d2f SYNC: fixed E2e test 2025-11-25 11:09:40 +00:00
SPRINX0\prochazka
497aaf6143 v6.7.1-premium-beta.5 2025-11-25 10:34:09 +01:00
SPRINX0\prochazka
9d6db3a93b v6.7.1-beta.4 2025-11-25 10:33:47 +01:00
CI workflows
8a6f3e6809 chore: auto-update github workflows 2025-11-25 09:22:33 +00:00
CI workflows
6c419716a4 Update pro ref 2025-11-25 09:22:15 +00:00
SPRINX0\prochazka
d1a769205c SYNC: support additional objects in team files 2025-11-25 09:22:02 +00:00
Stela Augustinova
b784e342c9 Translation - added translation tags for import/export, connection tab 2025-11-25 10:07:41 +01:00
CI workflows
0d82fd51c7 chore: auto-update github workflows 2025-11-25 08:21:06 +00:00
CI workflows
3b4d905485 Update pro ref 2025-11-25 08:20:50 +00:00
SPRINX0\prochazka
53c63f0f4b SYNC: diagrams supported in team files 2025-11-25 08:20:39 +00:00
SPRINX0\prochazka
5553e3cd8d SYNC: Handle error when saving to team files 2025-11-24 16:35:08 +00:00
Stela Augustinova
1e195e07e0 Translation - added translation tags for import/export 2025-11-24 17:02:34 +01:00
Stela Augustinova
b6f0e15951 Translation - add translation tags for logs 2025-11-24 16:48:29 +01:00
Stela Augustinova
da224303fe Translation - added translation tags for widgets 2025-11-24 16:29:29 +01:00
SPRINX0\prochazka
67ee130a9e japanese localization 2025-11-24 16:20:59 +01:00
CI workflows
18d908fa63 chore: auto-update github workflows 2025-11-24 14:40:35 +00:00
CI workflows
c61f58854e Update pro ref 2025-11-24 14:40:16 +00:00
SPRINX0\prochazka
f789ecd2f1 SYNC: japanese settings 2025-11-24 14:40:04 +00:00
CI workflows
1fdc30804a chore: auto-update github workflows 2025-11-24 14:31:03 +00:00
CI workflows
5ba10d0acb Update pro ref 2025-11-24 14:30:50 +00:00
SPRINX0\prochazka
12803d8154 SYNC: admin settings 2025-11-24 14:30:38 +00:00
CI workflows
36c391ccff chore: auto-update github workflows 2025-11-24 14:27:45 +00:00
CI workflows
765fb6297c Update pro ref 2025-11-24 14:27:06 +00:00
SPRINX0\prochazka
66255769ad SYNC: support for browser default language 2025-11-24 14:26:54 +00:00
CI workflows
04a8d38641 chore: auto-update github workflows 2025-11-24 14:20:00 +00:00
CI workflows
859d020031 Update pro ref 2025-11-24 14:19:44 +00:00
SPRINX0\prochazka
3c541117d0 SYNC: change language for Team Premium 2025-11-24 14:19:33 +00:00
SPRINX0\prochazka
80ca2e5215 Add the LANG environment variable for the web version. #1266 2025-11-24 14:26:25 +01:00
SPRINX0\prochazka
19f2aa2997 smaller upgrade button #1244 2025-11-24 10:57:57 +01:00
Jan Prochazka
ec657f30c7 Merge pull request #1268 from dbgate/feature/sort-sql
Feature/sort sql
2025-11-24 09:04:06 +01:00
SPRINX0\prochazka
7e84d495f5 sort by fix 2025-11-24 09:02:46 +01:00
SPRINX0\prochazka
c3baedd93c sort tables by size/rowCount 2025-11-24 08:58:49 +01:00
SPRINX0\prochazka
ae9676f744 sql object sort WIP 2025-11-21 16:37:12 +01:00
SPRINX0\prochazka
7ec156a5d1 table rows, table size in Oracle 2025-11-21 16:19:51 +01:00
SPRINX0\prochazka
b80cbea1bc show mongo collection sizes #552 2025-11-21 16:07:27 +01:00
SPRINX0\prochazka
4600fa9f32 Show table size fo MySQL and Postgres #552 2025-11-21 15:56:26 +01:00
SPRINX0\prochazka
6e0b3e5cdc fixed: Check updates option no longer available in 6.7.0 #1263 2025-11-21 15:18:00 +01:00
SPRINX0\prochazka
519ff87f5d Merge branch 'feature/mongo-legacy' 2025-11-21 12:57:31 +01:00
SPRINX0\prochazka
d4a363e37e v6.7.1-premium-beta.3 2025-11-21 10:46:26 +01:00
SPRINX0\prochazka
a3cfc45fef legacy mongodb optimalization + test fix 2025-11-21 10:44:06 +01:00
SPRINX0\prochazka
60602e02d9 logging app language 2025-11-20 17:31:18 +01:00
SPRINX0\prochazka
44366f7872 v6.7.1-premium-beta.2 2025-11-20 16:26:40 +01:00
SPRINX0\prochazka
2f18d8c204 fixed package version 2025-11-20 16:26:26 +01:00
SPRINX0\prochazka
08efbee52b italian translation 2025-11-20 16:14:56 +01:00
SPRINX0\prochazka
eac8d78c5d v6.7.1-premium-beta.1 2025-11-20 15:38:04 +01:00
SPRINX0\prochazka
db73673374 Connection to MognoDB legacy 2025-11-20 15:35:09 +01:00
SPRINX0\prochazka
281cdb7264 "Show foreign key hints" showing only in premium 2025-11-20 14:18:51 +01:00
SPRINX0\prochazka
101c80d820 fixed: A MERGE statement must be terminated by a semi-colon (;), but dbgate stripped it. #1257 2025-11-20 12:59:54 +01:00
SPRINX0\prochazka
1e06f65d9e portugese (brasil) translation 2025-11-19 18:16:59 +01:00
SPRINX0\prochazka
eea85709ed fixed error in auth login 2025-11-19 17:03:22 +01:00
SPRINX0\prochazka
cbca974529 v6.7.0 2025-11-19 14:22:56 +01:00
SPRINX0\prochazka
fc27f57580 changelog 2025-11-19 14:22:13 +01:00
SPRINX0\prochazka
e50dd6606e es tgranslations fixed 2025-11-19 14:21:13 +01:00
SPRINX0\prochazka
8c9e232d65 improved zh translation 2025-11-19 13:57:52 +01:00
SPRINX0\prochazka
ef98888394 fr translations improved 2025-11-19 13:53:46 +01:00
SPRINX0\prochazka
e4b9ba34df german translation polished 2025-11-19 12:52:40 +01:00
SPRINX0\prochazka
169e0ec9df sorted translation keys 2025-11-19 12:22:03 +01:00
SPRINX0\prochazka
2779353a32 renamed loc key 2025-11-19 12:18:38 +01:00
SPRINX0\prochazka
35aabb987c cs translations 2025-11-19 12:15:37 +01:00
SPRINX0\prochazka
06f02070c7 language in webapp stored in local storage 2025-11-19 10:11:24 +01:00
SPRINX0\prochazka
d138d3e786 changelog 2025-11-19 09:22:49 +01:00
SPRINX0\prochazka
dbea68d33a Fix horizontal scrolling on macOS trackpad/Magic Mouse #1250 2025-11-19 08:45:32 +01:00
SPRINX0\prochazka
72bcabf615 v6.6.14-beta.6 2025-11-19 08:02:22 +01:00
SPRINX0\prochazka
5db68eac24 SYNC: added localizations tests (+screenshots) 2025-11-18 17:15:08 +00:00
SPRINX0\prochazka
7d9d88860e changelog 2025-11-18 17:34:27 +01:00
SPRINX0\prochazka
06c80ad982 Chinese localization #347 #705 #939 #1079 2025-11-18 16:16:39 +01:00
SPRINX0\prochazka
c4335527f8 changelog WIP 2025-11-18 16:15:09 +01:00
SPRINX0\prochazka
90d27a2ad8 v6.6.14-premium-beta.6 2025-11-18 15:27:08 +01:00
SPRINX0\prochazka
93fd0a9af0 v6.6.14-beta.5 2025-11-18 15:26:52 +01:00
SPRINX0\prochazka
987b0aeb41 es treanslation in settings 2025-11-18 15:26:38 +01:00
SPRINX0\prochazka
8dc5ac0b25 v6.6.14-beta.4 2025-11-18 15:23:25 +01:00
SPRINX0\prochazka
d310a47523 v6.6.14-premium-beta.3 2025-11-18 15:23:14 +01:00
SPRINX0\prochazka
bd8fa3776d es translation + fixed language change 2025-11-18 15:22:39 +01:00
SPRINX0\prochazka
305796af53 spanish translation 2025-11-18 14:56:38 +01:00
SPRINX0\prochazka
60c10a69a3 french translation 2025-11-18 14:41:38 +01:00
SPRINX0\prochazka
c8aad9839f v6.6.14-premium-beta.2 2025-11-18 14:19:48 +01:00
SPRINX0\prochazka
64016a1326 v6.6.13-beta.1 2025-11-18 14:19:48 +01:00
SPRINX0\prochazka
a489c7ad8e added german translation 2025-11-18 14:19:48 +01:00
Jan Prochazka
afb9ba7ad6 Merge pull request #1259 from dbgate/feature/electron-translation
Feature/electron translation
2025-11-18 14:02:43 +01:00
SPRINX0\prochazka
b1de5b1120 fixed command palette 2025-11-18 13:45:02 +01:00
SPRINX0\prochazka
144a23e89b fixed condition 2025-11-18 13:11:47 +01:00
SPRINX0\prochazka
a6763a3e5d fixed main menu 2025-11-18 13:05:22 +01:00
SPRINX0\prochazka
f047ec787a translated electron menu 2025-11-18 12:29:48 +01:00
Stela Augustinova
d80c368ccb translation- tabs panel, menu 2025-11-18 10:34:44 +01:00
SPRINX0\prochazka
9b60173b8c horizontal menu translation 2025-11-18 10:14:36 +01:00
SPRINX0\prochazka
8556974ef1 translation WIP 2025-11-18 09:25:39 +01:00
Stela Augustinova
edf9f3a2be translation - settings, menu 2025-11-18 09:25:01 +01:00
SPRINX0\prochazka
7bb9414be8 electron menu translation WIP 2025-11-18 09:20:56 +01:00
SPRINX0\prochazka
6e439adb51 changelog 2025-11-18 08:05:19 +01:00
Jan Prochazka
f6b783e74a SYNC: fixed paste license key 2025-11-17 06:43:17 +00:00
SPRINX0\prochazka
171b81461c SYNC: fixed: Export CSV broken #1080 2025-11-14 09:17:06 +00:00
SPRINX0\prochazka
83db76aed8 v6.6.12-premium-beta.6 2025-11-13 15:28:26 +01:00
Jan Prochazka
e6d1bb7e5c Merge pull request #1258 from dbgate/feature/postgresql-export-bytea
Feature/postgresql export bytea
2025-11-13 15:27:28 +01:00
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
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
SPRINX0\prochazka
a7a6c664c8 SYNC: #1220 fixed typo 2025-10-21 10:42:26 +02:00
Pavel
fc9677f419 fix: wrap <tr> into <tbody> 2025-08-19 15:23:58 +02:00
Pavel
975a551728 feat: updgrade svlete to v4 2025-08-19 15:23:58 +02:00
683 changed files with 54936 additions and 12755 deletions

View File

@@ -1,12 +1,14 @@
---
name: Bug report
about: Create a report to help us improve DbGate
about: Create a report to help us improve DbGate (in ENGLISH)
title: 'BUG: Say something here'
labels: ''
assignees: ''
---
Please keep communication in ENGLISH to reach more contributors.
**Describe the bug**
A clear and concise description of what the bug is.

View File

@@ -1,12 +1,14 @@
---
name: Feature request
about: Suggest an idea for DbGate
about: Suggest an idea for DbGate (in ENGLISH)
title: 'FEAT: '
labels: ''
assignees: ''
---
Please keep communication in ENGLISH to reach more contributors.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

View File

@@ -1,12 +1,14 @@
---
name: Question
about: Ask a question about how to do something
about: Ask a question about how to do something (in ENGLISH)
title: 'QUESTION: Summary of your question'
labels: ''
assignees: ''
---
Please keep communication in ENGLISH to reach more contributors.
**Details:**
Details about your question

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:
@@ -17,6 +21,10 @@ jobs:
- windows-2022
- ubuntu-22.04
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade pip setuptools
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
@@ -60,21 +68,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

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:
@@ -17,6 +21,10 @@ jobs:
- windows-2022
- ubuntu-22.04
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade pip setuptools
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
@@ -56,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

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:
@@ -17,6 +21,10 @@ jobs:
- windows-2022
- ubuntu-22.04
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade pip setuptools
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
@@ -39,7 +47,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -87,23 +95,61 @@ jobs:
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

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:
@@ -17,6 +21,10 @@ jobs:
- windows-2022
- ubuntu-22.04
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade pip setuptools
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
@@ -39,7 +47,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -87,23 +95,61 @@ jobs:
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

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:
@@ -17,6 +21,10 @@ jobs:
- windows-2022
- ubuntu-22.04
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install --upgrade pip setuptools
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
@@ -56,24 +64,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

View File

@@ -39,7 +39,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -90,14 +90,6 @@ jobs:
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run `packer init` for Azure
run: |
cd ../dbgate-merged/packer
packer init ./azure-ubuntu.pkr.hcl
- name: Run `packer build` for Azure
run: |
cd ../dbgate-merged/packer
packer build ./azure-ubuntu.pkr.hcl
- name: Run `packer init` for AWS
run: |
cd ../dbgate-merged/packer
@@ -114,16 +106,6 @@ jobs:
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
- name: Delete old Azure VMs
run: |
cd ../dbgate-merged/packer
chmod +x delete-old-azure-images.sh
./delete-old-azure-images.sh
env:
AZURE_CLIENT_ID: ${{secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{secrets.AZURE_CLIENT_SECRET}}
AZURE_TENANT_ID: ${{secrets.AZURE_TENANT_ID}}
AZURE_SUBSCRIPTION_ID: ${{secrets.AZURE_SUBSCRIPTION_ID}}
- name: Delete old AMIs (AWS)
run: |
cd ../dbgate-merged/packer

View File

@@ -44,7 +44,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro

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: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- 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@11.5.1
- name: Remove dbmodel - should be not published
run: |
cd ..
@@ -71,28 +69,35 @@ jobs:
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
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"

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,103 +29,110 @@ 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@11.5.1
- name: yarn install
run: |
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
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 rest
working-directory: packages/rest
run: |
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"

View File

@@ -5,10 +5,14 @@ name: Cypress tests with screenshots PREMIUM
'on':
push:
branches:
- stable
- master
- develop
- feature/**
- hotfix/**
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-tests:
runs-on: ubuntu-latest
@@ -26,7 +30,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -69,8 +73,8 @@ jobs:
with:
name: screenshots
path: screenshots
- name: Push E2E screenshots
if: ${{ github.ref_name == 'master' }}
- name: Push E2E screenshots - stable
if: ${{ github.ref_name == 'stable' }}
run: |
git config --global user.email "info@dbgate.info"
git config --global user.name "GitHub Actions"
@@ -80,6 +84,17 @@ jobs:
git add .
git commit --amend --no-edit
git push --force
- name: Push E2E screenshots - master
if: ${{ github.ref_name == 'master' }}
run: |
git config --global user.email "info@dbgate.info"
git config --global user.name "GitHub Actions"
git clone https://${{ secrets.DIFLOW_GIT_SECRET }}@github.com/dbgate/dbgate-img.git
cp ../dbgate-merged/e2e-tests/screenshots/*.png dbgate-img/static/img-dev
cd dbgate-img/static/img-dev
git add .
git commit --amend --no-edit
git push --force
services:
postgres-cypress:
image: postgres
@@ -117,6 +132,10 @@ jobs:
image: redis
ports:
- '16011:6379'
dynamodb:
image: amazon/dynamodb-local
ports:
- '16015:8000'
mssql:
image: mcr.microsoft.com/mssql/server
ports:

View File

@@ -9,6 +9,9 @@ name: Integration and unit tests
- develop
- feature/**
- hotfix/**
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
all-tests:
runs-on: ubuntu-latest
@@ -20,46 +23,51 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
mv dbgate-pro/* ../dbgate-pro/
cd ..
mkdir dbgate-merged
cd dbgate-pro
cd sync
yarn
node sync.js --nowatch
cd ..
- name: yarn install
run: |
cd ../dbgate-merged
yarn install
- name: Integration tests
run: |
cd ../dbgate-merged
cd integration-tests
yarn test:ci
- name: Filter parser tests
if: always()
run: |
cd ../dbgate-merged
cd packages/filterparser
yarn test:ci
- name: Datalib (perspective) tests
if: always()
run: |
cd ../dbgate-merged
cd packages/datalib
yarn test:ci
- name: Tools tests
if: always()
run: |
cd ../dbgate-merged
cd packages/tools
yarn test:ci
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: integration-tests/result.json
action-name: Integration tests
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/datalib/result.json
action-name: Datalib (perspectives) test results
services:
postgres-integr:
image: postgres
@@ -113,3 +121,14 @@ jobs:
FIREBIRD_USE_LEGACY_AUTH: true
ports:
- '3050:3050'
mongodb:
image: mongo:4.0.12
ports:
- '27017:27017'
volumes:
- mongo-data:/data/db
- mongo-config:/data/configdb
dynamodb:
image: amazon/dynamodb-local
ports:
- '8000:8000'

1
.gitignore vendored
View File

@@ -24,6 +24,7 @@ docker/plugins
.env.development.local
.env.test.local
.env.production.local
.env.translation
npm-debug.log*
yarn-debug.log*

View File

@@ -2,5 +2,10 @@
"jestrunner.jestCommand": "node_modules/.bin/cross-env DEVMODE=1 LOCALTEST=1 node_modules/.bin/jest",
"cSpell.words": [
"dbgate"
]
],
"chat.tools.terminal.autoApprove": {
"yarn workspace": true,
"yarn --cwd packages/rest": true,
"yarn --cwd packages/web": true
}
}

9
AGENTS.md Normal file
View File

@@ -0,0 +1,9 @@
# AGENTS
## Rules
- In newly added code, always use `DBGM-00000` for message/error codes; do not introduce new numbered DBGM codes such as `DBGM-00316`.
- GUI uses Svelte4 (packages/web)
- GUI is tested with E2E tests in `e2e-tests` folder, using Cypress. Use data-testid attribute in components to make them easier to test.
- data-testid format: ComponentName_identifier. Use reasonable identifiers
- don't change content of storageModel.js - this is generated from table YAMLs with "yarn storage-json" command

File diff suppressed because it is too large Load Diff

119
CLAUDE.md Normal file
View File

@@ -0,0 +1,119 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
DbGate is a cross-platform (no)SQL database manager supporting MySQL, PostgreSQL, SQL Server, Oracle, MongoDB, Redis, SQLite, and more. It runs as a web app (Docker/NPM), an Electron desktop app, or in a browser. The monorepo uses Yarn workspaces.
## Development Commands
```sh
yarn # install all packages (also builds TS libraries and plugins)
yarn start # run API (port 3000) + web (port 5001) concurrently
```
For more control, run these 3 commands in separate terminals:
```sh
yarn start:api # Express API on port 3000
yarn start:web # Svelte frontend on port 5001
yarn lib # watch-compile TS libraries and plugins
```
For Electron development:
```sh
yarn start:web # web on port 5001
yarn lib # watch TS libs/plugins
yarn start:app # Electron app
```
### Building
```sh
yarn build:lib # build all TS libraries (sqltree, tools, filterparser, datalib, rest)
yarn build:api # build API
yarn build:web # build web frontend
yarn ts # TypeScript type-check API and web
yarn prettier # format all source files
```
### Testing
Unit tests (in packages like `dbgate-tools`):
```sh
yarn workspace dbgate-tools test
```
Integration tests (requires Docker for database containers):
```sh
cd integration-tests
yarn test:local # run all tests
yarn test:local:path __tests__/alter-database.spec.js # run a single test file
```
E2E tests (Cypress):
```sh
yarn cy:open # open Cypress UI
cd e2e-tests && yarn cy:run:browse-data # run a specific spec headlessly
```
## Architecture
### Monorepo Structure
| Path | Package | Purpose |
|---|---|---|
| `packages/api` | `dbgate-api` | Express.js backend server |
| `packages/web` | `dbgate-web` | Svelte 4 frontend (built with Rolldown) |
| `packages/tools` | `dbgate-tools` | Shared TS utilities: SQL dumping, schema analysis, diffing, driver base classes |
| `packages/datalib` | `dbgate-datalib` | Grid display logic, changeset management, perspectives, chart definitions |
| `packages/sqltree` | `dbgate-sqltree` | SQL AST representation and dumping |
| `packages/filterparser` | `dbgate-filterparser` | Parses filter strings into SQL/Mongo conditions |
| `packages/rest` | `dbgate-rest` | REST connection support |
| `packages/types` | `dbgate-types` | TypeScript type definitions (`.d.ts` only) |
| `packages/aigwmock` | `dbgate-aigwmock` | Mock AI gateway server for E2E testing |
| `plugins/dbgate-plugin-*` | — | Database drivers and file format handlers |
| `app/` | — | Electron shell |
| `integration-tests/` | — | Jest-based DB integration tests (Docker) |
| `e2e-tests/` | — | Cypress E2E tests |
### API Backend (`packages/api`)
- Express.js server with controllers in `src/controllers/` — each file exposes REST endpoints via the `useController` utility
- Database connections run in child processes (`src/proc/`) to isolate crashes and long-running operations
- `src/shell/` contains stream-based data pipeline primitives (readers, writers, transforms) used for import/export and replication
- Plugin drivers are loaded dynamically via `requireEngineDriver`; each plugin in `plugins/` exports a driver conforming to `DriverBase` from `dbgate-tools`
### Frontend (`packages/web`)
- Svelte 4 components; builds with Rolldown (not Vite/Webpack)
- Global state in `src/stores.ts` using Svelte writable stores, with `writableWithStorage` / `writableWithForage` helpers for persistence
- API calls go through `src/utility/api.ts` (`apiCall`, `apiOff`, etc.) which handles auth, error display, and cache invalidation
- Tab system: each open editor/viewer is a "tab" tracked in `openedTabs` store; tab components live in `src/tabs/`
- Left-panel tree items are "AppObjects" in `src/appobj/`
- Metadata (table lists, column info) is loaded reactively via hooks in `src/utility/metadataLoaders.ts`
- Commands/keybindings are registered in `src/commands/`
### Plugin Architecture
Each `plugins/dbgate-plugin-*` package provides:
- **Frontend build** (`build:frontend`): bundled JS loaded by the web UI for query formatting, data rendering
- **Backend build** (`build:backend`): Node.js driver code loaded by the API for actual DB connections
Plugins are copied to `plugins/dist/` via `plugins:copydist` before building the app or Docker image.
### Key Conventions
- Error/message codes use `DBGM-00000` as placeholder — do not introduce new numbered `DBGM-NNNNN` codes
- Frontend uses **Svelte 4** (not Svelte 5)
- E2E test selectors use `data-testid` attribute with format `ComponentName_identifier`
- Prettier config: single quotes, 2-space indent, 120-char line width, trailing commas ES5
- Logging via `pinomin`; pipe through `pino-pretty` for human-readable output
### Translation System
```sh
yarn translations:extract # extract new strings
yarn translations:add-missing # add missing translations
yarn translations:check # check for issues
```

View File

@@ -16,12 +16,9 @@ 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://www.dbgate.io/download/)
* Looking for DbGate Community? **Download** from [dbgate.org](https://dbgate.org/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
@@ -64,7 +61,7 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
* Edit table schema, indexes, primary and foreign keys
* Compare and synchronize database structure
* ER diagram
* Light and dark theme, next themes available as plugins from github community
* Light and dark theme, next themes available from DbGate Cloud
* Huge support for work with related data - master/detail views, foreign key lookups, expanding columns from related tables in flat data view
* Query designer - visual SQL query builder without writing SQL code. Complex conditions like WHERE NOT EXISTS.
* Query perspectives innovative nested table view over complex relational data, something like query designer on MongoDB databases
@@ -93,12 +90,12 @@ Any contributions are welcome. If you want to contribute without coding, conside
* 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://www.dbgate.io/purchase/premium/) license
* 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)
* 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)
* Add a SQL script to [Public Knowledge Base](https://github.com/dbgate/dbgate-knowledge-base)
* Where a small coding is acceptable for you, you could [create plugin](https://docs.dbgate.io/plugin-development). Plugins for new themes can be created actually without JS coding
* Where a small coding is acceptable for you, you could [create plugin](https://docs.dbgate.io/plugin-development)
* Create a new custom theme and share it on [DbGate Cloud](https://github.com/dbgate/dbgate-knowledge-base/tree/master/folder-Themes)
Thank you!

View File

@@ -13,9 +13,9 @@
<p>DbGate is cross-platform database manager. It's designed to be simple to use and effective, when working with more databases simultaneously. But there are also many advanced features like schema compare, visual query designer, chart visualisation or batch export and import.</p>
</description>
<url type="homepage">https://dbgate.org/</url>
<url type="homepage">https://www.dbgate.io/</url>
<url type="vcs-browser">https://github.com/dbgate/dbgate</url>
<url type="contact">https://dbgate.org/about/</url>
<url type="contact">https://www.dbgate.io/contact/</url>
<url type="donation">https://github.com/sponsors/dbgate</url>
<url type="bugtracker">https://github.com/dbgate/dbgate/issues</url>

View File

@@ -1,6 +1,6 @@
{
"name": "dbgate",
"version": "6.0.0-alpha.1",
"version": "7.0.0-alpha.1",
"private": true,
"author": "Jan Prochazka <jenasoft.database@gmail.com>",
"description": "Opensource database administration tool",
@@ -117,7 +117,7 @@
"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",
@@ -128,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"
}
}

View File

@@ -31,6 +31,16 @@ let mainModule;
let appUpdateStatus = '';
let settingsJson = {};
function getTranslated(key) {
if (typeof key === 'string' && global.TRANSLATION_DATA?.[key]) {
return global.TRANSLATION_DATA?.[key];
}
if (typeof key?._transKey === 'string') {
return global.TRANSLATION_DATA?.[key._transKey] ?? key._transOptions?.defaultMessage;
}
return key;
}
process.on('uncaughtException', function (error) {
console.error('uncaughtException', error);
});
@@ -63,6 +73,7 @@ try {
let mainWindow;
let mainMenu;
let runCommandOnLoad = null;
let mainWindowMenuSet = false;
log.transports.file.level = 'debug';
autoUpdater.logger = log;
@@ -85,17 +96,22 @@ 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) {
return { skip: true };
}
if (!command) {
return { skip: true };
}
return {
id,
label: command ? command.menuName || command.toolbarName || command.name : id,
label: command
? getTranslated(command.menuName) || getTranslated(command.toolbarName) || getTranslated(command.name)
: id,
accelerator: formatKeyText(command ? command.keyText : undefined),
enabled: command ? command.enabled : false,
enabled: command ? command.enabled && (!disableAll || command.systemCommand) : false,
click() {
if (mainWindow) {
mainWindow.webContents.send('run-command', id);
@@ -107,14 +123,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 +145,7 @@ function buildMenu() {
{
label: 'DbGate',
submenu: [
commandItem({ command: 'about.show' }),
commandItem({ command: 'about.show' }, disableAll),
{ role: 'services' },
{ role: 'hide' },
{ role: 'hideOthers' },
@@ -145,22 +161,28 @@ 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;
const command = commands[key];
// rebuild menu
if (menu.label != command.text || menu.accelerator != command.keyText) {
mainMenu = buildMenu();
if (global.TRANSLATION_DATA && (menu.label != command.text || menu.accelerator != command.keyText)) {
mainMenu = buildMenu(isModalOpened || !!dbgatePage);
Menu.setApplicationMenu(mainMenu);
// mainWindow.setMenu(mainMenu);
if (!mainWindowMenuSet) {
mainWindow.setMenu(mainMenu);
mainWindowMenuSet = true;
}
return;
}
menu.enabled = command.enabled;
menu.enabled = command.enabled && !isModalOpened && !dbgatePage;
}
});
ipcMain.on('quit-app', async (event, arg) => {
@@ -303,6 +325,12 @@ ipcMain.on('check-for-updates', async (event, url) => {
autoUpdater.autoDownload = false;
autoUpdater.checkForUpdates();
});
ipcMain.on('translation-data', async (event, arg) => {
global.TRANSLATION_DATA = JSON.parse(arg);
mainMenu = buildMenu();
Menu.setApplicationMenu(mainMenu);
mainWindow.setMenu(mainMenu);
});
function fillMissingSettings(value) {
const res = {
@@ -372,6 +400,14 @@ function createWindow() {
},
});
mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
{ urls: ['https://*.tile.openstreetmap.org/*'] },
(details, callback) => {
details.requestHeaders['Referer'] = 'https://www.dbgate.io';
callback({ requestHeaders: details.requestHeaders });
}
);
if (initialConfig['winIsMaximized']) {
mainWindow.maximize();
}
@@ -379,8 +415,8 @@ function createWindow() {
mainWindow.setFullScreen(true);
}
mainMenu = buildMenu();
mainWindow.setMenu(mainMenu);
// mainMenu = buildMenu();
// mainWindow.setMenu(mainMenu);
function loadMainWindow() {
const startUrl =

View File

@@ -1,6 +1,10 @@
module.exports = ({ editMenu, isMac }) => [
function _t(key, { defaultMessage, currentTranslations } = {}) {
return (currentTranslations || global.TRANSLATION_DATA)?.[key] || defaultMessage;
}
module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
{
label: 'File',
label: _t('menu.file', { defaultMessage: 'File', currentTranslations }),
submenu: [
{ command: 'new.connection', hideDisabled: true },
{ command: 'new.sqliteDatabase', hideDisabled: true },
@@ -28,7 +32,7 @@ module.exports = ({ editMenu, isMac }) => [
},
editMenu
? {
label: 'Edit',
label: _t('menu.edit', { defaultMessage: 'Edit', currentTranslations }),
submenu: [
{ command: 'edit.undo' },
{ command: 'edit.redo' },
@@ -53,7 +57,7 @@ module.exports = ({ editMenu, isMac }) => [
// ],
// },
{
label: 'View',
label: _t('menu.view', { defaultMessage: 'View', currentTranslations }),
submenu: [
{ command: 'app.reload', hideDisabled: true },
{ command: 'app.toggleDevTools', hideDisabled: true },
@@ -72,10 +76,12 @@ module.exports = ({ editMenu, isMac }) => [
{ command: 'app.zoomIn', hideDisabled: true },
{ command: 'app.zoomOut', hideDisabled: true },
{ command: 'app.zoomReset', hideDisabled: true },
{ divider: true },
{ command: 'app.showLogs', hideDisabled: true },
],
},
{
label: 'Tools',
label: _t('menu.tools', { defaultMessage: 'Tools', currentTranslations }),
submenu: [
{ command: 'database.search', hideDisabled: true },
{ command: 'commandPalette.show', hideDisabled: true },
@@ -91,6 +97,8 @@ module.exports = ({ editMenu, isMac }) => [
{ divider: true },
{ command: 'app.exportConnections', hideDisabled: true },
{ command: 'app.importConnections', hideDisabled: true },
{ divider: true },
{ command: 'app.managePlugins', hideDisabled: true },
],
},
...(isMac
@@ -102,7 +110,7 @@ module.exports = ({ editMenu, isMac }) => [
]
: []),
{
label: 'Help',
label: _t('menu.help', { defaultMessage: 'Help', currentTranslations }),
submenu: [
{ command: 'app.openDocs', hideDisabled: true },
{ command: 'app.openWeb', hideDisabled: true },
@@ -114,7 +122,7 @@ module.exports = ({ editMenu, isMac }) => [
{ command: 'tabs.changelog', hideDisabled: true },
{ command: 'about.show', hideDisabled: true },
{ divider: true },
{ command: 'file.checkForUpdates', hideDisabled: true },
{ command: 'app.checkForUpdates', hideDisabled: true },
],
},
];

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"

110
common/fixYmlHashes.js Normal file
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);
});

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;
}

View File

@@ -160,4 +160,31 @@ program
}
});
program
.command('sort')
.description('Sort translation files by keys')
.action(() => {
try {
const languages = getAllNonDefaultLanguages();
for (const language of languages) {
const filePath = `./translations/${language}.json`;
const content = fs.readFileSync(filePath, 'utf-8');
const translations = JSON.parse(content);
const sortedTranslations = {};
Object.keys(translations)
.sort()
.forEach(key => {
// @ts-ignore
sortedTranslations[key] = translations[key];
});
fs.writeFileSync(filePath, JSON.stringify(sortedTranslations, null, 2), 'utf-8');
console.log(`Sorted translations for language: ${language}`);
}
} catch (error) {
console.error(error);
console.error('Error during sort:', error.message);
process.exit(1);
}
});
module.exports = { program };

View File

@@ -0,0 +1,133 @@
require('dotenv').config({ path: '.env.translation' });
const fs = require('fs');
const path = require('path');
const OpenAI = require('openai');
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const translationsDir = path.join(__dirname, '../../translations');
const enFilePath = path.join(translationsDir, 'en.json');
const languageNames = {
'cs.json': 'Czech',
'de.json': 'German',
'es.json': 'Spanish',
'fr.json': 'French',
'it.json': 'Italian',
'ja.json': 'Japanese',
'ko.json': 'Korean',
'pt.json': 'Portuguese',
'sk.json': 'Slovak',
'zh.json': 'Chinese'
};
// Read source (english)
const enTranslations = JSON.parse(fs.readFileSync(enFilePath, 'utf8'));
const enKeys = Object.keys(enTranslations);
// Get all translation files
const translationFiles = fs.readdirSync(translationsDir)
.filter(file => file.endsWith('.json') && file !== 'en.json')
.sort();
console.log(`Found ${enKeys.length} keys in en.json\n`);
console.log('='.repeat(80));
async function translateMissingIds({file, translations, missingIds}){
const languageName = languageNames[file];
if (!languageName) {
console.log(`No language name mapping for file: ${file}`);
return;
}
// Build object with only missing translations
const needed = {};
missingIds.forEach(key => {
needed[key] = enTranslations[key];
});
// Get all existing translations as style examples
const existingTranslations = {};
Object.keys(translations).forEach(key => {
if (translations[key] && !translations[key].startsWith('***')) {
existingTranslations[key] = {
en: enTranslations[key],
translated: translations[key]
};
}
});
const prompt = `You are a professional translator for DbGate, a database management application.
Translate the following English UI strings to ${languageName}.
IMPORTANT RULES:
1. Preserve ALL placeholders exactly as they appear: {plugin}, {columnNumber}, {0}, {1}, etc.
2. Maintain technical terminology appropriately for database software
3. Match the translation style, tone, and formality of the existing translations shown below
4. Keep the same level of brevity or verbosity as the existing translations
5. Return ONLY valid JSON - no markdown, no explanations, no code blocks
6. Use the same keys as provided
EXISTING TRANSLATIONS (for style reference):
${JSON.stringify(existingTranslations, null, 2)}
STRINGS TO TRANSLATE:
${JSON.stringify(needed, null, 2)}
Return format: {"key": "translated value", ...}`;
const response = await client.chat.completions.create({
model: 'gpt-5.1',
messages: [
{ role: 'system', content: 'You are a professional translator specializing in software localization. Match the style and tone of existing translations. Return only valid JSON.' },
{ role: 'user', content: prompt }
],
temperature: 0.2
});
let translatedJson = response.choices[0].message.content.trim();
// Remove markdown code blocks if present
translatedJson = translatedJson.replace(/^```json\n?/, '').replace(/\n?```$/, '');
return JSON.parse(translatedJson);
}
(async () => {
for (const file of translationFiles) {
const filePath = path.join(translationsDir, file);
const translations = JSON.parse(fs.readFileSync(filePath, 'utf8'));
const missingIds = enKeys.filter(key => !translations.hasOwnProperty(key) || (typeof translations[key] === 'string' && translations[key].startsWith('***')));
console.log(`\n${file.toUpperCase()}`);
console.log('-'.repeat(80));
if (missingIds.length === 0) {
console.log('✓ All translations complete!');
continue;
} else {
console.log(`Found ${missingIds.length} untranslated IDs\n`);
}
const newTranslations = await translateMissingIds({file, translations, missingIds});
if (!newTranslations) {
console.log(`Skipping file due to translation error: ${file}`);
continue;
}
for (const [key, value] of Object.entries(newTranslations)) {
translations[key] = value;
console.log(`Translated: ${key} => ${value}`);
}
fs.writeFileSync(filePath, JSON.stringify(translations, null, 2) + '\n', 'utf8');
console.log(`\n✓ Updated translations written to ${file}`);
}
console.log('\n' + '='.repeat(80));
console.log('Translation complete!\n');
})();

View File

@@ -4,6 +4,7 @@ const volatilePackages = [
'@clickhouse/client',
'bson', // this package is already bundled and is used in mongodb
'mongodb',
'mongodb-old',
'mongodb-client-encryption',
'tedious',
'msnodesqlv8',

View File

@@ -4,5 +4,6 @@ module.exports = {
mssql: true,
oracle: true,
sqlite: true,
mongo: true
mongo: true,
dynamo: true,
};

View File

@@ -3,8 +3,58 @@ const os = require('os');
const fs = require('fs');
const baseDir = path.join(os.homedir(), '.dbgate');
const testApiPidFile = path.join(__dirname, 'tmpdata', 'test-api.pid');
const aigwmockPidFile = path.join(__dirname, 'tmpdata', 'aigwmock.pid');
function readProcessStartTime(pid) {
if (process.platform === 'linux') {
try {
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
return stat.split(' ')[21] || null;
} catch (err) {
return null;
}
}
return null;
}
function isPidStillOurs(meta) {
if (!meta || !(meta.pid > 0)) return false;
if (process.platform === 'linux' && meta.startTime) {
const current = readProcessStartTime(meta.pid);
return current === meta.startTime;
}
return true;
}
function stopProcessByPidFile(pidFile) {
if (!fs.existsSync(pidFile)) return;
try {
const content = fs.readFileSync(pidFile, 'utf-8').trim();
let meta;
try {
meta = JSON.parse(content);
} catch (_) {
const pid = Number(content);
meta = Number.isInteger(pid) && pid > 0 ? { pid } : null;
}
if (isPidStillOurs(meta)) {
process.kill(meta.pid);
}
} catch (err) {
// ignore stale PID files and dead processes
}
try {
fs.unlinkSync(pidFile);
} catch (err) {
// ignore cleanup errors
}
}
function clearTestingData() {
stopProcessByPidFile(testApiPidFile);
stopProcessByPidFile(aigwmockPidFile);
if (fs.existsSync(path.join(baseDir, 'connections-e2etests.jsonl'))) {
fs.unlinkSync(path.join(baseDir, 'connections-e2etests.jsonl'));
}

View File

@@ -10,6 +10,7 @@ module.exports = defineConfig({
// baseUrl: 'http://localhost:3000',
// trashAssetsBeforeRuns: false,
chromeWebSecurity: false,
reporter: process.env.CI ? 'mocha-reporter-gha' : 'spec',
setupNodeEvents(on, config) {
// implement node event listeners here
@@ -36,6 +37,9 @@ module.exports = defineConfig({
case 'browse-data':
serverProcess = exec('yarn start:browse-data');
break;
case 'rest':
serverProcess = exec('yarn start:rest');
break;
case 'team':
serverProcess = exec('yarn start:team');
break;
@@ -48,6 +52,12 @@ module.exports = defineConfig({
case 'charts':
serverProcess = exec('yarn start:charts');
break;
case 'redis':
serverProcess = exec('yarn start:redis');
break;
case 'ai-chat':
serverProcess = exec('yarn start:ai-chat');
break;
}
await waitOn({ resources: ['http://localhost:3000'] });

View File

@@ -113,6 +113,18 @@ describe('Add connection', () => {
cy.contains('performance_schema');
});
it('Plugin tab', () => {
cy.testid('WidgetIconPanel_menu').click();
cy.contains('Tools').click();
cy.contains('Manage plugins').click();
cy.contains('dbgate-plugin-theme-total-white').click();
// text from plugin markdown
cy.contains('Total white theme');
// wait for load logos
cy.wait(2000);
cy.themeshot('view-plugin-tab');
});
it('export connections', () => {
cy.testid('WidgetIconPanel_menu').click();
cy.contains('Tools').click();

View File

@@ -0,0 +1,105 @@
Cypress.on('uncaught:exception', err => {
if (err.message.includes("Failed to execute 'importScripts' on 'WorkerGlobalScope'")) {
return false;
}
});
beforeEach(() => {
cy.visit('http://localhost:3000');
cy.viewport(1250, 900);
});
describe('Database Chat (MySQL)', () => {
it('Database chat - chart of popular genres', () => {
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 - find most popular artist', () => {
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.contains('Iron Maiden', { timeout: 30000 });
cy.themeshot('database-chat-popular-artist');
});
});
describe('GraphQL Chat', () => {
it('GraphQL chat - list users', () => {
cy.contains('REST GraphQL').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_graphqlChat').click();
cy.wait(1000);
cy.get('body').realType('list all users');
cy.get('body').realPress('Enter');
cy.testid('GraphQlChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.contains('users', { timeout: 30000 });
cy.themeshot('graphql-chat-list-users');
});
it('GraphQL chat - product categories chart', () => {
cy.contains('REST GraphQL').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_graphqlChat').click();
cy.wait(1000);
cy.get('body').realType('show me a chart of product categories');
cy.get('body').realPress('Enter');
cy.testid('GraphQlChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.testid('chart-canvas', { timeout: 30000 }).should($c =>
expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/)
);
cy.themeshot('graphql-chat-categories-chart');
});
it('GraphQL chat - find most expensive product', () => {
cy.contains('REST GraphQL').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_graphqlChat').click();
cy.wait(1000);
cy.get('body').realType('find the most expensive product');
cy.get('body').realPress('Enter');
cy.testid('GraphQlChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.contains('products', { timeout: 30000 });
cy.themeshot('graphql-chat-expensive-product');
});
it('GraphQL chat - show all categories', () => {
cy.contains('REST GraphQL').click();
cy.testid('TabsPanel_buttonNewObject').click();
cy.testid('NewObjectModal_graphqlChat').click();
cy.wait(1000);
cy.get('body').realType('show all categories');
cy.get('body').realPress('Enter');
cy.testid('GraphQlChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.contains('categories', { timeout: 30000 });
cy.themeshot('graphql-chat-all-categories');
});
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');
});
});

View File

@@ -60,7 +60,7 @@ describe('Data browser data', () => {
cy.contains('MyChinook').click();
cy.testid('SqlObjectList_search').clear().type('album');
cy.contains('Tables (1/11)');
cy.contains('347 rows, InnoDB');
cy.contains('347 rows, 65.5 KB, InnoDB');
cy.testid('SqlObjectList_searchMenuDropDown').click();
cy.contains('Column name').click();
cy.contains('Tables (2/11)');
@@ -85,14 +85,16 @@ describe('Data browser data', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('Album').click();
// hide what is not needed
cy.testid('WidgetIconPanel_database').click();
cy.testid('DataGrid_itemReferences').click();
cy.testid('DataFilterControl_input_Title').type('Rock{enter}');
cy.contains('Rows: 7');
cy.testid('DataFilterControl_input_AlbumId').type('>10xxx{enter}');
cy.contains('Rows: 7');
cy.testid('DataFilterControl_filtermenu_Title').click();
// hide what is not needed
cy.testid('WidgetIconPanel_database').click();
cy.testid('DataGrid_itemReferences').click();
cy.testid('DataFilterControl_filtermenu_ArtistId').click();
cy.themeshot('data-browser-filter');
cy.testid('DataGridCore_button_clearFilters').click();
cy.contains('Rows: 347');
@@ -202,7 +204,7 @@ describe('Data browser data', () => {
cy.themeshot('query-editor-join-wizard');
});
it('Mongo JSON data view', () => {
it('Mongo query JSON data view', () => {
cy.contains('Mongo-connection').click();
cy.contains('MgChinook').click();
cy.contains('Customer').click();
@@ -213,9 +215,10 @@ describe('Data browser data', () => {
cy.contains('Open query').click();
cy.wait(1000);
cy.contains('Execute').click();
cy.testid('WidgetIconPanel_cell-data').click();
cy.testid('TabContent_1').contains('Leonie').rightclick();
cy.contains('Show cell data').click();
// test JSON view
cy.contains('Country: "Brazil"');
cy.contains('Country: "Germany"');
cy.themeshot('mongo-query-json-view');
});
@@ -293,7 +296,8 @@ describe('Data browser data', () => {
// cy.contains('location').click();
cy.contains('14.2').click();
cy.contains('13.9').click({ shiftKey: true });
cy.testid('WidgetIconPanel_cell-data').click();
cy.testid('WidgetIconPanel_database').click();
cy.testid('TableDataTab_toggleCellDataView').click();
cy.wait(2000);
cy.themeshot('cell-map-view');
});
@@ -310,17 +314,6 @@ describe('Data browser data', () => {
cy.themeshot('search-in-connections');
});
it('Plugin tab', () => {
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Manage plugins').click();
cy.contains('dbgate-plugin-theme-total-white').click();
// text from plugin markdown
cy.contains('Total white theme');
// wait for load logos
cy.wait(2000);
cy.themeshot('view-plugin-tab');
});
it('Edit mongo data JSON', () => {
// TODO FIX: Missing button+ctx menu Revert all changes, missing button+ctx menu add document
// TODO: Dark theme - not visible changed and deleted document
@@ -348,7 +341,7 @@ describe('Data browser data', () => {
cy.themeshot('save-changes-mongodb');
});
it('Edit mongo data JSON', () => {
it('Mongo JSON cell view', () => {
// TODO FIX: Auto expand cell view
cy.contains('Mongo-connection').click();
cy.contains('MgRivers').click();
@@ -358,7 +351,8 @@ describe('Data browser data', () => {
cy.testid('ColumnManagerRow_checkbox_countries.1').click();
cy.testid('ColumnManagerRow_checkbox__id').click();
cy.testid('DataFilterControl_input_countries.1').type('EXISTS{enter}');
cy.testid('WidgetIconPanel_cell-data').click();
cy.contains('Austria').click();
cy.testid('CollectionDataTab_toggleCellDataView').click();
cy.themeshot('mongodb-json-cell-view');
});
@@ -483,4 +477,78 @@ describe('Data browser data', () => {
cy.testid('DataDeployTab_importIntoDb').click();
cy.themeshot('data-replicator');
});
it('Form cell view', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('Invoice').click();
cy.contains('Rows: 412');
cy.get('[data-row="0"][data-col="header"]').click();
cy.get('[data-row="1"][data-col="header"]').click();
cy.get('[data-row="0"][data-col="header"]').click();
cy.contains('Autodetect - Form');
cy.themeshot('form-cell-view');
});
it('Group by', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('Album').click();
cy.testid('WidgetIconPanel_database').click();
cy.testid('ColumnHeaderControl_dropdown_ArtistId').click();
cy.contains('Group by').click();
cy.testid('ColumnHeaderControl_dropdown_Title').first().click();
cy.themeshot('data-browser-group-by');
});
it('Filter by expanded column', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('Album').click();
cy.testid('WidgetIconPanel_database').click();
cy.testid('ColumnManagerRow_expand_ArtistId').click();
cy.testid('ColumnManagerRow_checkbox_ArtistId.Name').click();
cy.testid('ColumnManagerRow_checkbox_ArtistId').click();
cy.testid('DataFilterControl_input_ArtistId.Name').type('mich{enter}');
cy.themeshot('data-browser-filter-by-expanded');
});
it('DynamoDB', () => {
cy.contains('Dynamo-connection').click();
cy.contains('us-east-1').click();
cy.contains('Album').click();
cy.contains('Pearl Jam').click();
cy.themeshot('dynamodb-table-data');
cy.contains('Switch to JSON').click();
cy.themeshot('dynamodb-json-view');
cy.contains('Customer').click();
cy.testid('DataFilterControl_input_CustomerId').type('<=10{enter}');
cy.contains('Rows: 10');
cy.wait(1000);
cy.contains('Helena').click().rightclick();
cy.contains('Show cell data').click();
cy.contains('City: "Prague"');
cy.themeshot('dynamodb-query-json-view');
cy.contains('Switch to JSON').click();
cy.contains('Leonie').rightclick();
cy.contains('Edit document').click();
Array.from({ length: 11 }).forEach(() => cy.realPress('ArrowDown'));
Array.from({ length: 14 }).forEach(() => cy.realPress('ArrowRight'));
Array.from({ length: 7 }).forEach(() => cy.realPress('Delete'));
cy.realType('Italy');
cy.testid('EditJsonModal_saveButton').click();
cy.contains('Helena').rightclick();
cy.contains('Delete document').click();
cy.contains('Save').click();
cy.themeshot('dynamodb-save-changes');
cy.testid('SqlObjectList_addButton').click();
cy.contains('New collection/container').click();
cy.themeshot('dynamodb-new-collection');
});
});

View File

@@ -110,61 +110,127 @@ describe('Charts', () => {
cy.themeshot('new-object-window');
});
it('Database chat - charts', () => {
it('Switch language', () => {
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.wait(5000);
cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
cy.themeshot('database-chat-chart');
cy.testid('WidgetIconPanel_settings').click();
cy.testid('SettingsModal_languageSelect').select('Deutsch');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Sprache');
cy.themeshot('switch-language-de');
cy.testid('SettingsModal_languageSelect').select('Français');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Langue');
cy.themeshot('switch-language-fr');
cy.testid('SettingsModal_languageSelect').select('Español');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Idioma');
cy.themeshot('switch-language-es');
cy.testid('SettingsModal_languageSelect').select('Čeština');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Jazyk');
cy.themeshot('switch-language-cs');
cy.testid('SettingsModal_languageSelect').select('中文');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('语言');
cy.themeshot('switch-language-zh');
cy.testid('SettingsModal_languageSelect').select('English');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_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: 30000 }).click();
cy.wait(30000);
// cy.contains('Iron Maiden');
cy.themeshot('database-chat');
it('Settings', () => {
cy.testid('WidgetIconPanel_settings').click();
cy.themeshot('app-settings-general');
// 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');
cy.contains('Behaviour').click();
cy.themeshot('app-settings-behaviour');
cy.get('[data-testid=BehaviourSettings_useTabPreviewMode]').uncheck();
// SQL Editor
cy.contains('SQL Editor').click();
cy.get('[data-testid=SQLEditorSettings_sqlCommandsCase]').select('lowerCase');
cy.contains('MySql-connection').click();
cy.contains('charts_sample').click();
cy.contains('employees').click();
cy.contains('MyChinook').click();
cy.contains('Customer').rightclick();
cy.contains('SQL template').click();
cy.contains('CREATE TABLE').click();
cy.contains('create table');
// Default Actions
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Default Actions').click();
cy.get('[data-testid=DefaultActionsSettings_useLastUsedAction]').uncheck();
// Themes
cy.contains('Themes').click();
cy.themeshot('app-settings-themes');
cy.testid('ThemeSkeleton-Dark-built-in').click();
cy.testid('ThemeSkeleton-Light-built-in').click();
// General
cy.contains(/^General$/).click();
cy.contains('charts_sample');
cy.get('[data-testid=GeneralSettings_lockedDatabaseMode]').check();
cy.contains('Connections').click();
cy.contains('charts_sample').should('not.exist');
// Datagrid
cy.contains('Data grid').click();
cy.get('[data-testid=DataGridSettings_showHintColumns]').uncheck();
cy.wait(500);
cy.contains('Album').click();
cy.contains('AC/DC').should('not.exist');
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Keyboard shortcuts').click();
cy.themeshot('app-settings-keyboard-shortcuts');
cy.contains('Chart').click();
cy.testid('CommandModal_keyboardButton').click();
cy.realPress(['Control', 'g']);
cy.realPress('Enter');
cy.contains('OK').click();
cy.contains('Ctrl+G');
cy.contains('AI').click();
cy.themeshot('app-settings-ai');
cy.get('[data-testid=AISettings_addProviderButton]').click();
cy.contains('Provider 1');
cy.get('[data-testid=AiProviderCard_removeButton]').click();
cy.contains('Are you sure you want to remove Provider 1 provider?');
cy.contains('OK').click();
cy.contains('Provider 1').should('not.exist');
});
it('Explain query error', () => {
it('Custom theme', () => {
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Themes').click();
cy.testid('ThemeSettings-themeList').contains('Green-Sample').click();
cy.testid('WidgetIconPanel_file').click();
cy.themeshot('green-theme', { keepTheme: true });
cy.testid('ThemeSettings-themeList').contains('Solarized-light').click();
cy.testid('WidgetIconPanel_database').click();
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.contains('Customer').click();
cy.contains('Leonie');
cy.testid('WidgetIconPanel_file').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.wait(5000);
// cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
// cy.themeshot('database-chat-chart');
cy.themeshot('solarized-theme', { keepTheme: true });
});
});

View File

@@ -3,6 +3,8 @@ const { formatQueryWithoutParams } = require('dbgate-tools');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
'dbgate-datalib': require('dbgate-datalib'),
};
function requireEngineDriver(engine) {
@@ -50,6 +52,9 @@ function multiTest(testProps, testDefinition) {
if (localconfig.mongo && !testProps.skipMongo) {
it('MongoDB', () => testDefinition('Mongo-connection', 'my_guitar_shop', 'mongo@dbgate-plugin-mongo'));
}
if (localconfig.dynamo && !testProps.skipMongo) {
it('DynamoDB', () => testDefinition('Dynamo-connection', null, 'dynamodb@dbgate-plugin-dynamodb'));
}
}
describe('Transactions', () => {
@@ -103,13 +108,70 @@ describe('Transactions', () => {
describe('Backup table', () => {
multiTest({ skipMongo: true }, (connectionName, databaseName, engine, options = {}) => {
const implicitTransactions = options.implicitTransactions ?? false;
cy.contains(connectionName).click();
if (databaseName) cy.contains(databaseName).click();
cy.contains('customers').rightclick();
cy.contains('addresses').rightclick();
cy.contains('Create table backup').click();
cy.testid('ConfirmSqlModal_okButton').click();
cy.contains('_customers').click();
cy.contains('Rows: 8').should('be.visible');
cy.testid('app-object-group-items-table-backups').contains('addresses').click();
cy.contains('Rows: 12').should('be.visible');
cy.testid('app-object-group-items-tables').contains('addresses').click();
cy.contains('Ridgewood').click();
cy.testid('TableDataTab_deleteSelectedRows').click();
cy.contains('Rosewood').click();
cy.testid('TableDataTab_deleteSelectedRows').click();
cy.contains('Vermont').click();
cy.get('body').realType('Wermont{enter}');
cy.testid('TableDataTab_insertNewRow').click();
cy.get('body').realType('Modranska{enter}');
cy.realPress(['ArrowLeft']);
cy.realPress(['ArrowLeft']);
cy.get('body').realType('13{enter}');
cy.realPress(['ArrowRight']);
cy.get('body').realType('1{enter}');
cy.realPress(['ArrowRight']);
cy.realPress(['ArrowRight']);
cy.realPress(['ArrowRight']);
cy.get('body').realType('Prague{enter}');
cy.realPress(['ArrowRight']);
cy.get('body').realType('CZ{enter}');
cy.realPress(['ArrowRight']);
cy.get('body').realType('10000{enter}');
cy.realPress(['ArrowRight']);
cy.get('body').realType('111222333{enter}');
cy.testid('TableDataTab_save').click();
cy.testid('ConfirmSqlModal_okButton', { timeout: 10000 }).click();
cy.contains('Rows: 11').should('be.visible'); // wait for save
cy.testid('app-object-group-items-table-backups').contains('addresses').rightclick();
cy.contains('restore script').click();
cy.contains('UPDATE'); // wait for query
cy.testid('QueryTab_executeButton').click();
cy.contains('Query execution finished');
if (implicitTransactions) {
cy.testid('QueryTab_commitTransactionButton').click();
cy.contains('Commit Transaction finished');
}
cy.realPress('F1');
cy.realType('Close all');
cy.realPress('Enter');
// cy.testid('CloseTabModal_buttonConfirm').click();
cy.wait(1000);
cy.testid('app-object-group-items-tables').contains('addresses', { timeout: 10000 }).click();
// check whether data was successfully restored
cy.contains('Rows: 12').should('be.visible');
cy.contains('Ridgewood');
cy.contains('Vermont');
});
});
@@ -146,13 +208,15 @@ describe('Import CSV', () => {
cy.contains('Import').click();
cy.get('input[type=file]').selectFile('cypress/fixtures/customers-20.csv', { force: true });
cy.contains('customers-20');
cy.testid('ImportExportConfigurator_tableMappingSection').contains('customers-20');
cy.testid('ImportExportTab_preview_content').contains('50ddd99fAdF48B3').should('be.visible');
cy.testid('ImportExportTab_executeButton').click();
cy.contains('20 rows written').should('be.visible');
cy.testid('ImportExportConfigurator_tableMappingSection').contains('20 rows written').should('be.visible');
cy.testid('SqlObjectList_refreshButton').click();
cy.testid('DatabasStatusMenu_refreshFull').click();
// cy.contains('Refresh DB structure (incremental)').click();
cy.testid('SqlObjectList_container').contains('customers-20').click();
cy.contains('Rows: 20').should('be.visible');
@@ -178,7 +242,7 @@ describe('Import CSV - source error', () => {
cy.testid('ImportExportTab_preview_content').contains('Invalid Closing Quote').should('be.visible');
cy.testid('ImportExportTab_executeButton').click();
cy.testid('ImportExportConfigurator_errorInfoIcon_customers-20-err').click();
cy.testid('ImportExportConfigurator_errorInfoIcon_customers-20-err', { timeout: 10000 }).click();
cy.testid('ErrorMessageModal_message').contains('Invalid Closing Quote').should('be.visible');
});
@@ -197,7 +261,7 @@ describe('Import CSV - target error', () => {
cy.contains('customers-20');
cy.testid('ImportExportConfigurator_targetName_customers-20').clear().type('system."]`');
cy.testid('ImportExportTab_executeButton').click();
cy.testid('ImportExportConfigurator_errorInfoIcon_customers-20').click();
cy.testid('ImportExportConfigurator_errorInfoIcon_customers-20', { timeout: 10000 }).click();
cy.testid('ErrorMessageModal_message').should('be.visible');
});
});

View File

@@ -0,0 +1,120 @@
Cypress.on('uncaught:exception', (err, runnable) => {
// if the error message matches the one about WorkerGlobalScope importScripts
if (err.message.includes("Failed to execute 'importScripts' on 'WorkerGlobalScope'")) {
// return false to let Cypress know we intentionally want to ignore this error
return false;
}
// otherwise let Cypress throw the error
});
beforeEach(() => {
cy.visit('http://localhost:3000');
cy.viewport(1250, 900);
});
describe('Redis data', () => {
it('String test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('app').click();
cy.contains('version').click();
cy.testid('RedisValueDetail_AceEditor').click().realPress('Backspace').realType('1');
cy.contains('Save').click();
cy.contains('OK').click();
});
it('Hash test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('user').click();
cy.contains('alice').click();
cy.testid('RedisKeyDetailTab_RenameKeyButton').click();
cy.themeshot('redis-rename-key');
cy.realType('3');
cy.contains('OK').click();
cy.contains('age').click();
cy.testid('RedisValueHashDetail_ValueSection').click().realPress('Backspace').realType('8');
cy.contains('Add field').click();
cy.testid('RedisValueListLikeEdit_key').click().realType('phone');
cy.testid('RedisValueListLikeEdit_value').click().realType('123-456-7890');
cy.contains('Refresh').click();
cy.themeshot('redis-hash-edit');
cy.contains('Save').click();
cy.themeshot('redis-hash-script-edit');
cy.contains('OK').click();
});
it('List test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('queue').click();
cy.contains('emails').click();
cy.contains('Add field').click();
cy.testid('RedisValueListLikeEdit_value').click().realType('reset');
cy.contains('Save').click();
cy.contains('OK').click();
});
it('Set test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('tags').click();
cy.contains('Add field').click();
cy.testid('RedisValueListLikeEdit_value').click().realType('newtag');
cy.contains('Save').click();
cy.contains('OK').click();
});
it('ZSet test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('leaderboard').click();
cy.contains('alice').click();
cy.testid('RedisValueZSetDetail_score')
.click()
.realPress('Backspace')
.realPress('Backspace')
.realPress('Backspace')
.realType('35');
cy.contains('Save').click();
cy.contains('OK').click();
cy.contains('35').should('exist');
});
it('JSON test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('user').click();
cy.contains('1:*').click();
cy.contains('json').click();
cy.testid('RedisValueDetail_displaySelect').select('JSON view');
cy.themeshot('redis-json-detail');
});
it('Stream test', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.contains('events').click();
cy.contains('Add field').click();
cy.testid('RedisValueListLikeEdit_field').click().realType('message');
cy.testid('RedisValueListLikeEdit_value').click().realType('Hello, World!');
cy.contains('Save').click();
cy.contains('OK').click();
cy.themeshot('redis-stream');
});
it('Add key', () => {
cy.contains('Redis-connection').click();
cy.contains('db1').click();
cy.testid('RedisKeysTree_addKeyDropdown').click();
cy.contains('String').click();
cy.testid('NewRedisKeyTab_keyName').click().realType('newstringkey');
cy.testid('RedisValueDetail_AceEditor').click().realType('This is a new string key.');
cy.contains('Save').click();
cy.contains('OK').click();
cy.contains('newstringkey').should('exist');
cy.testid('RedisKeysTree_addKeyDropdown').click();
cy.contains('Hash').click();
cy.themeshot('redis-add-hash-key');
});
});

View File

@@ -0,0 +1,39 @@
Cypress.on('uncaught:exception', err => {
if (err.message.includes("Failed to execute 'importScripts' on 'WorkerGlobalScope'")) {
return false;
}
});
beforeEach(() => {
cy.visit('http://localhost:3000');
cy.viewport(1250, 900);
});
describe('REST API connections', () => {
it('GraphQL test', () => {
cy.contains('REST GraphQL').click();
cy.contains('products').click();
cy.testid('GraphQlExplorerNode_toggle_products').click();
cy.testid('GraphQlExplorerNode_checkbox_products.name').click();
cy.testid('GraphQlExplorerNode_checkbox_products.price').click();
cy.testid('GraphQlExplorerNode_checkbox_products.description').click();
cy.testid('GraphQlExplorerNode_checkbox_products.category').click();
cy.testid('GraphQlQueryTab_execute').click();
cy.contains('Electronics');
cy.themeshot('rest-graphql-query');
});
it('REST OpenAPI test', () => {
cy.contains('REST OpenAPI').click();
cy.contains('/api/categories').click();
cy.testid('RestApiEndpointTab_execute').click();
cy.contains('Electronics');
cy.themeshot('rest-openapi-query');
});
it('REST OData test', () => {
cy.contains('REST OData').click();
cy.contains('/Users').click();
cy.testid('ODataEndpointTab_execute').click();
cy.contains('Henry');
cy.themeshot('rest-odata-query');
});
});

View File

@@ -36,9 +36,11 @@ Cypress.Commands.add(
prevSubject: 'optional',
},
(subject, file, options) => {
cy.window().then(win => {
win.__changeCurrentTheme('theme-dark');
});
if (!options?.keepTheme) {
cy.window().then(win => {
win.__changeCurrentTheme('dark');
});
}
// cy.screenshot(`${file}-dark`, {
// onAfterScreenshot: (doc, props) => {
@@ -63,9 +65,11 @@ Cypress.Commands.add(
// });
// });
cy.window().then(win => {
win.__changeCurrentTheme('theme-light');
});
if (!options?.keepTheme) {
cy.window().then(win => {
win.__changeCurrentTheme('light');
});
}
if (subject) {
cy.wrap(subject).screenshot(`${file}-light`, options);

View File

@@ -0,0 +1,253 @@
{
"themeName": "Green-Sample",
"themeType": "light",
"themeVariables": {
"--theme-generic-font": "oklch(27% 0.07 130)",
"--theme-generic-font-hover": "oklch(40% 0.15 130)",
"--theme-generic-font-grayed": "oklch(65% 0.05 130)",
"--theme-link-foreground": "oklch(40% 0.25 130)",
"--theme-content-background": "oklch(95% 0.05 130)",
"--theme-widget-panel-background": "oklch(80% 0.1 130)",
"--theme-widget-panel-foreground": "oklch(27% 0.07 130)",
"--theme-widget-icon-background-active": "oklch(50% 0.12 130)",
"--theme-widget-icon-foreground-active": "white",
"--theme-widget-icon-foreground-hover": "white",
"--theme-widget-icon-border-active": "1px solid white",
"--theme-scrollbar-background": "oklch(90% 0.08 130)",
"--theme-scrollbar-thumb-background": "oklch(70% 0.12 130)",
"--theme-scrollbar-thumb-background-hover": "oklch(40% 0.15 130)",
"--theme-scrollbar-corner-background": "oklch(85% 0.1 130)",
"--theme-tabs-panel-border": "1px solid oklch(95% 0.05 130)",
"--theme-tabs-panel-foreground": "oklch(20% 0.06 130)",
"--theme-tabs-panel-active-foreground": "oklch(10% 0.06 130)",
"--theme-tabs-panel-background": "oklch(95.5% 0.04 130)",
"--theme-tabs-panel-active-background": "oklch(80% 0.12 130)",
"--theme-tabs-panel-item-background": "oklch(90% 0.1 130)",
"--theme-tabs-panel-active-border": "1px solid oklch(50% 0.2 130)",
"--theme-splitter-active": "oklch(50% 0.2 130)",
"--theme-splitter-button-background": "oklch(90% 0.1 130)",
"--theme-splitter-button-background-active": "oklch(85% 0.15 130)",
"--theme-splitter-button-foreground": "oklch(10% 0.06 130)",
"--theme-sidebar-background": "oklch(90% 0.1 130)",
"--theme-sidebar-background-hover": "oklch(80% 0.12 130)",
"--theme-sidebar-background-active": "oklch(75% 0.14 130)",
"--theme-sidebar-background-focused": "oklch(70% 0.18 130)",
"--theme-sidebar-foreground": "oklch(20% 0.06 130)",
"--theme-sidebar-foreground-button": "oklch(40% 0.12 130)",
"--theme-sidebar-foreground-grayed": "oklch(65% 0.05 130)",
"--theme-sidebar-foreground-hover": "oklch(50% 0.25 130)",
"--theme-sidebar-section-background": "oklch(65% 0.05 130)",
"--theme-sidebar-section-border": "none",
"--theme-sidebar-section-border-top": "1px solid oklch(80% 0.1 130)",
"--theme-sidebar-section-foreground": "oklch(10% 0.06 130)",
"--theme-sidebar-border": "none",
"--theme-altsidebar-background": "oklch(95% 0.05 130)",
"--theme-altsidebar-background-grayed": "oklch(97% 0.02 130)",
"--theme-altsidebar-background-hover": "oklch(85% 0.1 130)",
"--theme-altsidebar-background-active": "oklch(80% 0.12 130)",
"--theme-altsidebar-background-focused": "oklch(75% 0.15 130)",
"--theme-altsidebar-foreground": "oklch(20% 0.06 130)",
"--theme-altsidebar-foreground-button": "oklch(40% 0.12 130)",
"--theme-altsidebar-foreground-grayed": "oklch(65% 0.05 130)",
"--theme-altsidebar-foreground-hover": "oklch(50% 0.25 130)",
"--theme-altsidebar-section-background": "oklch(97% 0.02 130)",
"--theme-altsidebar-section-border": "none",
"--theme-altsidebar-section-border-top": "1px solid oklch(85% 0.1 130)",
"--theme-altsidebar-section-foreground": "oklch(10% 0.06 130)",
"--theme-altsidebar-border": "1px solid oklch(90% 0.1 130)",
"--theme-searchbox-background": "oklch(80% 0.12 130)",
"--theme-searchbox-placeholder": "oklch(65% 0.05 130)",
"--theme-searchbox-border": "1px solid oklch(70% 0.15 130)",
"--theme-searchbox-background-filtered": "oklch(95% 0.04 110)",
"--theme-altsearchbox-background": "oklch(90% 0.1 130)",
"--theme-altsearchbox-placeholder": "oklch(65% 0.05 130)",
"--theme-altsearchbox-border": "1px solid oklch(80% 0.1 130)",
"--theme-inlinebutton-foreground": "oklch(40% 0.12 130)",
"--theme-inlinebutton-foreground-disabled": "oklch(65% 0.05 130)",
"--theme-inlinebutton-foreground-hover": "black",
"--theme-inlinebutton-circle-hover-background": "oklch(85% 0.1 130)",
"--theme-inlinebutton-bordered-border": "1px solid oklch(85% 0.1 130)",
"--theme-inlinebutton-bordered-hover-border": "1px solid oklch(70% 0.15 130)",
"--theme-inlinebutton-bordered-background": "linear-gradient(to bottom, oklch(95% 0.04 130) 5%, oklch(90% 0.1 130) 100%)",
"--theme-inlinebutton-bordered-hover-background": "linear-gradient(to bottom, oklch(90% 0.1 130) 5%, oklch(95% 0.04 130) 100%)",
"--theme-datagrid-background": "oklch(95% 0.04 130)",
"--theme-datagrid-foreground": "oklch(20% 0.06 130)",
"--theme-datagrid-foreground-grayed": "oklch(65% 0.05 130)",
"--theme-datagrid-border-horizontal": "1px solid oklch(90% 0.1 130)",
"--theme-datagrid-border-vertical": "1px solid oklch(95% 0.04 130)",
"--theme-datagrid-cell-background": "oklch(97% 0.02 130)",
"--theme-datagrid-headercell-background": "oklch(95% 0.04 130)",
"--theme-datagrid-cell-background-alt": "oklch(95% 0.04 130)",
"--theme-datagrid-cell-background-alt2": "oklch(90% 0.1 130)",
"--theme-datagrid-filter-background": "oklch(90% 0.1 130)",
"--theme-datagrid-filter-border": "1px solid oklch(85% 0.1 130)",
"--theme-datagrid-filter-ok-background": "oklch(95% 0.1 135)",
"--theme-datagrid-filter-error-background": "oklch(95% 0.12 30)",
"--theme-datagrid-modified-row-background": "oklch(95% 0.1 135)",
"--theme-datagrid-modified-cell-background": "oklch(90% 0.15 135)",
"--theme-datagrid-inserted-row-background": "oklch(95% 0.1 110)",
"--theme-datagrid-deleted-row-background": "oklch(95% 0.1 25)",
"--theme-datagrid-selected-cell-background": "oklch(80% 0.1 130)",
"--theme-datagrid-focused-cell-background": "oklch(75% 0.15 130)",
"--theme-datagrid-focused-cell-border-horizontal": "1px solid oklch(70% 0.2 130)",
"--theme-datagrid-focused-cell-border-vertical": "1px solid oklch(70% 0.2 130)",
"--theme-datagrid-selected-point-marker": "oklch(50% 0.25 130)",
"--theme-datagrid-corner-label-background": "oklch(75% 0.15 130)",
"--theme-datagrid-corner-label-border": "1px solid oklch(70% 0.2 130)",
"--theme-datagrid-detail-header-background": "oklch(85% 0.05 130)",
"--theme-datagrid-detail-header-border": "1px solid oklch(80% 0.1 130)",
"--theme-datagrid-cell-foreground-value-green": "oklch(45% 0.2 140)",
"--theme-checkbox-check": "oklch(90% 0.1 130)",
"--theme-checkbox-background": "oklch(40% 0.25 130)",
"--theme-checkbox-border": "1px solid oklch(70% 0.15 130)",
"--theme-checkbox-mark": "white",
"--theme-checkbox-background-disabled": "oklch(95% 0.04 130)",
"--theme-checkbox-background-disabled-before": "oklch(70% 0.15 130)",
"--theme-checkbox-hover-not-disabled": "oklch(65% 0.05 130)",
"--theme-checkbox-background-inherited": "oklch(85% 0.1 130)",
"--theme-table-border": "1px solid oklch(85% 0.1 130)",
"--theme-table-cell-background": "oklch(97% 0.02 130)",
"--theme-table-cell-empty-background": "oklch(95% 0.04 130)",
"--theme-table-cell-empty-foreground": "oklch(65% 0.05 130)",
"--theme-table-header-background": "oklch(95% 0.04 130)",
"--theme-table-selected-background": "oklch(75% 0.15 130)",
"--theme-table-active-background": "oklch(80% 0.1 130)",
"--theme-table-hover-background": "oklch(95% 0.04 130)",
"--theme-table-added-background": "oklch(95% 0.1 110)",
"--theme-table-changed-background": "oklch(95% 0.1 135)",
"--theme-table-deleted-background": "oklch(95% 0.1 25)",
"--theme-cell-active-border": "2px solid oklch(50% 0.25 130)",
"--theme-object-header-background": "oklch(95% 0.04 130)",
"--theme-modal-background": "oklch(97% 0.02 130)",
"--theme-modal-header-background": "oklch(85% 0.1 130)",
"--theme-modal-footer-background": "oklch(97% 0.02 130)",
"--theme-modal-border": "1px solid oklch(85% 0.1 130)",
"--theme-modal-overlay-background": "color-mix(in srgb, #124012 40%, transparent)",
"--theme-modal-shadow": "0 20px 25px -5px color-mix(in srgb, #124012 10%, transparent)",
"--theme-modal-close-hover-background": "oklch(70% 0.15 130)",
"--theme-formbutton-foreground": "white",
"--theme-formbutton-border": "1px solid oklch(40% 0.25 130)",
"--theme-formbutton-border-hover": "1px solid oklch(50% 0.3 130)",
"--theme-formbutton-border-active": "2px solid oklch(55% 0.35 130)",
"--theme-formbutton-background": "oklch(40% 0.25 130)",
"--theme-formbutton-background-disabled": "oklch(85% 0.1 130)",
"--theme-formbutton-border-disabled": "1px solid oklch(85% 0.1 130)",
"--theme-formbutton-foreground-disabled": "oklch(65% 0.05 130)",
"--theme-formbutton-background-hover": "oklch(35% 0.3 130)",
"--theme-formbutton-background-active": "oklch(35% 0.3 130)",
"--theme-outlinebutton-foreground": "oklch(10% 0.06 130)",
"--theme-outlinebutton-border": "1px solid oklch(40% 0.25 130)",
"--theme-outlinebutton-hover-foreground": "oklch(40% 0.25 130)",
"--theme-outlinebutton-hover-border": "2px solid oklch(50% 0.3 130)",
"--theme-tabs-control-background": "oklch(95% 0.04 130)",
"--theme-tabs-control-border": "1px solid oklch(90% 0.1 130)",
"--theme-tabs-control-selected-background": "oklch(98% 0.01 130)",
"--theme-tabs-control-selected-border": "2px solid oklch(50% 0.25 130)",
"--theme-inline-tabs-border": "1px solid oklch(90% 0.1 130)",
"--theme-inline-tabs-border-active": "2px solid oklch(50% 0.25 130)",
"--theme-toolstrip-background": "oklch(97% 0.02 130)",
"--theme-toolstrip-border": "1px solid oklch(90% 0.1 130)",
"--theme-toolstrip-button-foreground": "oklch(27% 0.07 130)",
"--theme-panel-border-subtle": "1px solid color-mix(in srgb, oklch(20% 0.06 130) 5%, transparent)",
"--theme-panel-type-label-color": "oklch(65% 0.05 130)",
"--theme-toolstrip-button-foreground-disabled": "oklch(65% 0.05 130)",
"--theme-toolstrip-button-foreground-icon": "oklch(40% 0.12 130)",
"--theme-toolstrip-button-background": "oklch(97% 0.02 130)",
"--theme-toolstrip-button-background-hover": "oklch(95% 0.04 130)",
"--theme-toolstrip-button-background-active": "oklch(90% 0.1 130)",
"--theme-toolstrip-button-border": "1px solid oklch(90% 0.1 130)",
"--theme-toolstrip-button-border-hover": "1px solid oklch(85% 0.1 130)",
"--theme-toolstrip-button-border-disabled": "1px solid oklch(90% 0.1 130)",
"--theme-toolstrip-button-split-separator-border": "1px solid oklch(85% 0.1 130)",
"--theme-designer-background": "oklch(97% 0.02 130)",
"--theme-designer-item-background": "oklch(95% 0.04 130)",
"--theme-designer-selection-marker": "oklch(35% 0.3 130)",
"--theme-designer-item-border": "1px solid oklch(90% 0.1 130)",
"--theme-designer-stroke-color": "oklch(65% 0.05 130)",
"--theme-designer-arrow-color": "oklch(27% 0.07 130)",
"--theme-designer-select-reactangle-foreground": "oklch(50% 0.25 130)",
"--theme-designer-header-background-1": "oklch(70% 0.15 130)",
"--theme-designer-header-background-2": "oklch(70% 0.18 180)",
"--theme-designer-header-background-3": "oklch(68% 0.15 100)",
"--theme-designer-header-background-grayed": "oklch(85% 0.1 130)",
"--theme-designer-close-background": "oklch(90% 0.1 130)",
"--theme-designer-close-background-hover": "oklch(85% 0.1 130)",
"--theme-designer-close-background-active": "oklch(70% 0.15 130)",
"--theme-designer-drag-column-background": "oklch(90% 0.2 110)",
"--theme-designer-select-column-background": "oklch(90% 0.1 130)",
"--theme-statusbar-background": "oklch(40% 0.25 130)",
"--theme-statusbar-foreground": "oklch(95% 0.04 130)",
"--theme-statusbar-background-hover": "oklch(35% 0.3 130)",
"--theme-statusbar-button-background": "oklch(85% 0.1 130)",
"--theme-statusbar-button-foreground": "oklch(27% 0.07 130)",
"--theme-statusbar-icon-error": "oklch(80% 0.1 25)",
"--theme-statusbar-icon-ok": "oklch(85% 0.2 130)",
"--theme-aichat-user-background": "oklch(93% 0.06 130)",
"--theme-aichat-assistant-background": "oklch(95% 0.04 130)",
"--theme-applog-details-background": "oklch(98% 0.01 130)",
"--theme-input-border": "1px solid oklch(85% 0.1 130)",
"--theme-input-border-hover": "1px solid oklch(70% 0.15 130)",
"--theme-input-border-hover-color": "oklch(70% 0.15 130)",
"--theme-input-border-focus": "1px solid oklch(50% 0.25 130)",
"--theme-input-border-focus-color": "oklch(50% 0.25 130)",
"--theme-input-border-disabled": "1px solid oklch(90% 0.1 130)",
"--theme-input-background": "white",
"--theme-input-foreground": "oklch(20% 0.06 130)",
"--theme-input-placeholder": "oklch(65% 0.05 130)",
"--theme-input-background-disabled": "oklch(95% 0.04 130)",
"--theme-input-foreground-disabled": "oklch(65% 0.05 130)",
"--theme-input-focus-ring": "0 0 0 3px color-mix(in srgb, oklch(50% 0.25 130) 10%, transparent)",
"--theme-input-multi-clear-background": "oklch(90% 0.1 130)",
"--theme-input-multi-clear-foreground": "oklch(40% 0.12 130)",
"--theme-input-multi-clear-hover": "oklch(85% 0.1 130)",
"--theme-input-shadow": "0 1px 2px 0 color-mix(in srgb, oklch(20% 0.06 130) 5%, transparent)",
"--theme-input-shadow-hover": "0 4px 6px -2px color-mix(in srgb, oklch(20% 0.06 130) 8%, transparent)",
"--theme-input-shadow-focus": "0 1px 2px 0 color-mix(in srgb, oklch(20% 0.06 130) 5%, transparent)",
"--theme-input-inplace-select-shadow": "0 1px 10px 1px oklch(40% 0.12 130)",
"--theme-color-selected-border": "2px solid oklch(27% 0.07 130)",
"--theme-new-object-button-background": "oklch(90% 0.1 130)",
"--theme-new-object-button-background-hover": "oklch(85% 0.1 130)",
"--theme-status-valid-background": "oklch(95% 0.1 110)",
"--theme-status-testing-background": "oklch(95% 0.1 135)",
"--theme-status-error-background": "oklch(95% 0.1 25)",
"--theme-status-unconfigured-background": "oklch(95% 0.04 130)",
"--theme-status-untested-background": "oklch(94% 0.1 65)",
"--theme-dropdown-icon-hover": "oklch(45% 0.3 130)",
"--theme-icon-picker-background": "oklch(90% 0.1 130)",
"--theme-icon-picker-border": "1px solid oklch(85% 0.1 130)",
"--theme-icon-picker-hover": "oklch(85% 0.1 130)",
"--theme-icon-picker-selected": "oklch(80% 0.15 130)",
"--theme-dbkey-background": "oklch(98% 0.01 130)",
"--theme-dbkey-border": "1px solid oklch(90% 0.1 130)",
"--theme-dbkey-icon-hover": "oklch(70% 0.15 130)",
"--theme-chip-background": "oklch(85% 0.1 130)",
"--theme-titlebar-background": "oklch(85% 0.1 130)",
"--theme-titlebar-button-hover": "oklch(70% 0.15 130)",
"--theme-card-background": "oklch(90% 0.1 130)",
"--theme-card-border": "1px solid oklch(85% 0.1 130)",
"--theme-content-background-hover": "oklch(95% 0.04 130)",
"--theme-admin-menu-item-hover": "oklch(95% 0.04 130)",
"--theme-admin-menu-item-active": "oklch(85% 0.1 130)",
"--theme-admin-menu-background": "oklch(90% 0.1 130)",
"--theme-admin-menu-border": "1px solid oklch(90% 0.1 130)",
"--theme-json-tree-string-color": "oklch(45% 0.3 110)",
"--theme-json-tree-symbol-color": "oklch(45% 0.3 110)",
"--theme-json-tree-boolean-color": "oklch(40% 0.25 130)",
"--theme-json-tree-function-color": "oklch(40% 0.25 130)",
"--theme-json-tree-number-color": "oklch(50% 0.3 130)",
"--theme-json-tree-label-color": "oklch(55% 0.3 140)",
"--theme-json-tree-arrow-color": "oklch(65% 0.05 130)",
"--theme-json-tree-null-color": "oklch(65% 0.05 130)",
"--theme-json-tree-undefined-color": "oklch(65% 0.05 130)",
"--theme-json-tree-date-color": "oklch(65% 0.05 130)",
"--theme-json-tree-deleted-background": "oklch(95% 0.1 25)",
"--theme-json-tree-modified-background": "oklch(95% 0.1 135)",
"--theme-json-tree-inserted-background": "oklch(95% 0.1 110)",
"--theme-icon-blue": "oklch(40% 0.25 130)",
"--theme-icon-green": "oklch(45% 0.2 140)",
"--theme-icon-red": "oklch(40% 0.3 25)",
"--theme-icon-gold": "oklch(50% 0.2 60)",
"--theme-icon-yellow": "oklch(50% 0.15 80)",
"--theme-icon-magenta": "oklch(45% 0.3 135)"
}
}

View File

@@ -0,0 +1,15 @@
HSET "actor:1000" "first_name" "Sandra"
HSET "actor:1000" "last_name" "Bullock"
HSET "actor:1000" "date_of_birth" "1964"
HSET "actor:1001" "first_name" "Jon"
HSET "actor:1001" "last_name" "Hamm"
HSET "actor:1001" "date_of_birth" "1971"
HSET "actor:1002" "first_name" "Allison"
HSET "actor:1002" "last_name" "Janney"
HSET "actor:1002" "date_of_birth" "1959"
HSET "actor:1003" "first_name" "Steve"
HSET "actor:1003" "last_name" "Coogan"
HSET "actor:1003" "date_of_birth" "1965"

View File

@@ -0,0 +1,14 @@
SET app:name "App"
SET app:version "1.0.0"
SET app:env "test"
SET user:1:json "{\"id\":1,\"name\":\"Alice\",\"email\":\"alice@app.test\",\"roles\":[\"admin\",\"user\"],\"settings\":{\"theme\":\"dark\",\"language\":\"sk\"}}"
SET user:2:json "{\"id\":2,\"name\":\"Bob\",\"email\":\"bob@app.test\",\"roles\":[\"user\"],\"settings\":{\"theme\":\"light\",\"language\":\"en\"}}"
RPUSH queue:emails "welcome" "reset-password" "newsletter" "promotion" "weekly-digest"
HSET user:alice name "Alice" email "alice@app.test" active "true" age "29" country "SK"
HSET user:bob name "Bob" email "bob@app.test" active "false" age "34" country "CZ"
SADD tags "app" "backend" "database" "redis" "test" "production"
ZADD leaderboard 100 "alice" 250 "bob" 180 "carol" 90 "dave" 300 "eve"
XADD events * type "login" userId "1" ip "127.0.0.1" device "web"
XADD events * type "update-profile" userId "1" field "email" old "alice@app.test" new "alice@new.app"
XADD events * type "login" userId "2" ip "10.0.0.5" device "mobile"
XADD events * type "logout" userId "1" reason "manual"

View File

@@ -5,14 +5,14 @@ services:
restart: always
environment:
POSTGRES_PASSWORD: Pwd2020Db
ports:
ports:
- 16000:5432
mariadb:
image: mariadb
command: --default-authentication-plugin=mysql_native_password
restart: always
ports:
ports:
- 16004:3306
environment:
- MYSQL_ROOT_PASSWORD=Pwd2020Db
@@ -20,21 +20,21 @@ services:
mysql-ssh-login:
build: containers/mysql-ssh-login
restart: always
ports:
ports:
- 16017:3306
- "16012:22"
- '16012:22'
mysql-ssh-keyfile:
build: containers/mysql-ssh-keyfile
restart: always
ports:
ports:
- 16007:3306
- "16008:22"
- '16008:22'
dex:
build: containers/dex
ports:
- "16009:5556"
- '16009:5556'
mongo:
image: mongo:4.4.29
@@ -50,6 +50,11 @@ services:
ports:
- 16011:6379
dynamodb:
image: amazon/dynamodb-local
ports:
- 16015:8000
mssql:
image: mcr.microsoft.com/mssql/server
restart: always

14
e2e-tests/env/ai-chat/.env vendored Normal file
View File

@@ -0,0 +1,14 @@
CONNECTIONS=mysql,graphql
LOCAL_AI_GATEWAY=true
LABEL_mysql=MySql-connection
SERVER_mysql=localhost
USER_mysql=root
PASSWORD_mysql=Pwd2020Db
PORT_mysql=16004
ENGINE_mysql=mysql@dbgate-plugin-mysql
LABEL_graphql=REST GraphQL
ENGINE_graphql=graphql@rest
APISERVERURL1_graphql=http://localhost:4444/graphql/noauth

View File

@@ -1,4 +1,4 @@
CONNECTIONS=mysql,postgres,mongo,redis
CONNECTIONS=mysql,postgres,mongo,dynamo
LABEL_mysql=MySql-connection
SERVER_mysql=localhost
@@ -23,7 +23,8 @@ PASSWORD_mongo=Pwd2020Db
PORT_mongo=16010
ENGINE_mongo=mongo@dbgate-plugin-mongo
LABEL_redis=Redis-connection
SERVER_redis=localhost
ENGINE_redis=redis@dbgate-plugin-redis
PORT_redis=16011
LABEL_dynamo=Dynamo-connection
SERVER_dynamo=localhost
PORT_dynamo=16015
AUTH_TYPE_dynamo=onpremise
ENGINE_dynamo=dynamodb@dbgate-plugin-dynamodb

View File

@@ -1,4 +1,4 @@
CONNECTIONS=mysql,postgres,mssql,oracle,sqlite,mongo
CONNECTIONS=mysql,postgres,mssql,oracle,sqlite,mongo,dynamo
LOG_CONNECTION_SENSITIVE_VALUES=true
LABEL_mysql=MySql-connection
@@ -43,3 +43,10 @@ PASSWORD_mongo=Pwd2020Db
PORT_mongo=16010
ENGINE_mongo=mongo@dbgate-plugin-mongo
LABEL_dynamo=Dynamo-connection
SERVER_dynamo=localhost
PORT_dynamo=16015
AUTH_TYPE_dynamo=onpremise
DATABASE_dynamo=localhost
ENGINE_dynamo=dynamodb@dbgate-plugin-dynamodb

6
e2e-tests/env/redis/.env vendored Normal file
View File

@@ -0,0 +1,6 @@
CONNECTIONS=redis
LABEL_redis=Redis-connection
SERVER_redis=localhost
ENGINE_redis=redis@dbgate-plugin-redis
PORT_redis=16011

14
e2e-tests/env/rest/.env vendored Normal file
View File

@@ -0,0 +1,14 @@
CONNECTIONS=odata,openapi,graphql
LABEL_odata=REST OData
ENGINE_odata=odata@rest
APISERVERURL1_odata=http://localhost:4444/odata/noauth
LABEL_openapi=REST OpenAPI
ENGINE_openapi=openapi@rest
APISERVERURL1_openapi=http://localhost:4444/openapi.json
APISERVERURL2_openapi=http://localhost:4444/openapi/noauth
LABEL_graphql=REST GraphQL
ENGINE_graphql=graphql@rest
APISERVERURL1_graphql=http://localhost:4444/graphql/noauth

168
e2e-tests/init/ai-chat.js Normal file
View File

@@ -0,0 +1,168 @@
const fs = require('fs');
const path = require('path');
const { spawn, spawnSync } = require('child_process');
const rootDir = path.resolve(__dirname, '..', '..');
const testApiDir = path.join(rootDir, 'test-api');
const aigwmockDir = path.join(rootDir, 'packages', 'aigwmock');
const tmpDataDir = path.resolve(__dirname, '..', 'tmpdata');
const testApiPidFile = path.join(tmpDataDir, 'test-api.pid');
const aigwmockPidFile = path.join(tmpDataDir, 'aigwmock.pid');
const isWindows = process.platform === 'win32';
const dbgateApi = require('dbgate-api');
dbgateApi.initializeApiEnvironment();
const dbgatePluginMysql = require('dbgate-plugin-mysql');
dbgateApi.registerPlugins(dbgatePluginMysql);
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// --- MySQL setup (same as charts init) ---
async function initMySqlDatabase(dbname, inputFile) {
const connection = {
server: process.env.SERVER_mysql,
user: process.env.USER_mysql,
password: process.env.PASSWORD_mysql,
port: process.env.PORT_mysql,
engine: 'mysql@dbgate-plugin-mysql',
};
await dbgateApi.executeQuery({
connection,
sql: `DROP DATABASE IF EXISTS ${dbname}`,
});
await dbgateApi.executeQuery({
connection,
sql: `CREATE DATABASE ${dbname}`,
});
await dbgateApi.importDatabase({
connection: { ...connection, database: dbname },
inputFile,
});
}
// --- Process management helpers ---
function readProcessStartTime(pid) {
if (process.platform === 'linux') {
try {
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
return stat.split(' ')[21] || null;
} catch (err) {
return null;
}
}
return null;
}
function isPidStillOurs(meta) {
if (!meta || !(meta.pid > 0)) return false;
if (process.platform === 'linux' && meta.startTime) {
const current = readProcessStartTime(meta.pid);
return current === meta.startTime;
}
return true;
}
function stopProcess(pidFile) {
if (!fs.existsSync(pidFile)) return;
try {
const content = fs.readFileSync(pidFile, 'utf-8').trim();
let meta;
try {
meta = JSON.parse(content);
} catch (_) {
const pid = Number(content);
meta = Number.isInteger(pid) && pid > 0 ? { pid } : null;
}
if (isPidStillOurs(meta)) {
process.kill(meta.pid);
}
} catch (err) {
// ignore stale pid or already terminated
}
try {
fs.unlinkSync(pidFile);
} catch (err) {
// ignore
}
}
function ensureDependencies(dir, checkFile) {
if (fs.existsSync(checkFile)) return;
const command = isWindows ? 'cmd.exe' : 'yarn';
const args = isWindows ? ['/c', 'yarn install --silent'] : ['install', '--silent'];
const result = spawnSync(command, args, {
cwd: dir,
stdio: 'inherit',
env: process.env,
});
if (result.status !== 0) {
throw new Error(`DBGM-00297 Failed to install dependencies in ${dir}`);
}
}
function startBackgroundProcess(dir, pidFile, port) {
const command = isWindows ? 'cmd.exe' : 'yarn';
const args = isWindows ? ['/c', 'yarn start'] : ['start'];
const child = spawn(command, args, {
cwd: dir,
env: { ...process.env, PORT: String(port) },
detached: true,
stdio: 'ignore',
});
child.unref();
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
const meta = { pid: child.pid };
const startTime = readProcessStartTime(child.pid);
if (startTime) meta.startTime = startTime;
fs.writeFileSync(pidFile, JSON.stringify(meta));
}
async function waitForReady(url, timeoutMs = 30000) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
try {
const response = await fetch(url);
if (response.ok) return;
} catch (err) {
// continue waiting
}
await delay(500);
}
throw new Error(`DBGM-00305 Server at ${url} did not start in time`);
}
// --- Main ---
async function run() {
// 1. Set up MyChinook MySQL database
console.log('[ai-chat init] Setting up MyChinook database...');
await initMySqlDatabase('MyChinook', path.resolve(path.join(__dirname, '../data/chinook-mysql.sql')));
// 2. Start test-api (GraphQL/REST server on port 4444)
console.log('[ai-chat init] Starting test-api on port 4444...');
stopProcess(testApiPidFile);
ensureDependencies(testApiDir, path.join(testApiDir, 'node_modules', 'swagger-jsdoc', 'package.json'));
startBackgroundProcess(testApiDir, testApiPidFile, 4444);
await waitForReady('http://localhost:4444/openapi.json');
console.log('[ai-chat init] test-api is ready');
// 3. Start aigwmock (AI Gateway mock on port 3110)
console.log('[ai-chat init] Starting aigwmock on port 3110...');
stopProcess(aigwmockPidFile);
ensureDependencies(aigwmockDir, path.join(aigwmockDir, 'node_modules', 'express', 'package.json'));
startBackgroundProcess(aigwmockDir, aigwmockPidFile, 3110);
await waitForReady('http://localhost:3110/openrouter/v1/models');
console.log('[ai-chat init] aigwmock is ready');
}
run().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -8,6 +8,8 @@ const dbgatePluginMysql = require('dbgate-plugin-mysql');
dbgateApi.registerPlugins(dbgatePluginMysql);
const dbgatePluginPostgres = require('dbgate-plugin-postgres');
dbgateApi.registerPlugins(dbgatePluginPostgres);
const dbgatePluginDynamodb = require('dbgate-plugin-dynamodb');
dbgateApi.registerPlugins(dbgatePluginDynamodb);
async function initMySqlDatabase(dbname, inputFile) {
await dbgateApi.executeQuery({
@@ -125,44 +127,32 @@ async function initMongoDatabase(dbname, inputDirectory) {
// });
}
async function initRedisDatabase(inputDirectory) {
await dbgateApi.executeQuery({
connection: {
server: process.env.SERVER_redis,
user: process.env.USER_redis,
password: process.env.PASSWORD_redis,
port: process.env.PORT_redis,
engine: 'redis@dbgate-plugin-redis',
},
sql: 'FLUSHALL',
});
async function initDynamoDatabase(inputDirectory) {
const dynamodbConnection = {
server: process.env.SERVER_dynamo,
port: process.env.PORT_dynamo,
authType: 'onpremise',
engine: 'dynamodb@dbgate-plugin-dynamodb',
};
const driver = dbgatePluginDynamodb.drivers.find(d => d.engine === 'dynamodb@dbgate-plugin-dynamodb');
const pool = await driver.connect(dynamodbConnection);
const collections = await driver.listCollections(pool);
for (const collection of collections) {
await driver.dropTable(pool, collection);
}
await driver.disconnect(pool);
for (const file of fs.readdirSync(inputDirectory)) {
await dbgateApi.executeQuery({
connection: {
server: process.env.SERVER_redis,
user: process.env.USER_redis,
password: process.env.PASSWORD_redis,
port: process.env.PORT_redis,
engine: 'redis@dbgate-plugin-redis',
database: 0,
},
sqlFile: path.join(inputDirectory, file),
// logScriptItems: true,
const pureName = path.parse(file).name;
const src = await dbgateApi.jsonLinesReader({ fileName: path.join(inputDirectory, file) });
const dst = await dbgateApi.tableWriter({
connection: dynamodbConnection,
pureName,
createIfNotExists: true,
});
await dbgateApi.copyStream(src, dst);
}
// await dbgateApi.importDatabase({
// connection: {
// server: process.env.SERVER_postgres,
// user: process.env.USER_postgres,
// password: process.env.PASSWORD_postgres,
// port: process.env.PORT_postgres,
// database: dbname,
// engine: 'postgres@dbgate-plugin-postgres',
// },
// inputFile,
// });
}
const baseDir = path.join(os.homedir(), '.dbgate');
@@ -188,7 +178,7 @@ async function run() {
await initMongoDatabase('MgChinook', path.resolve(path.join(__dirname, '../data/chinook-jsonl')));
await initMongoDatabase('MgRivers', path.resolve(path.join(__dirname, '../data/rivers-jsonl')));
await initRedisDatabase(path.resolve(path.join(__dirname, '../data/redis')));
await initDynamoDatabase(path.resolve(path.join(__dirname, '../data/chinook-jsonl')));
await copyFolder(
path.resolve(path.join(__dirname, '../data/chinook-jsonl')),

View File

@@ -90,6 +90,11 @@ async function run() {
path.join(baseDir, 'files-e2etests', 'sql')
);
await copyFolder(
path.resolve(path.join(__dirname, '../data/files/themes')),
path.join(baseDir, 'files-e2etests', 'themes')
);
await initMySqlDatabase('MyChinook', path.resolve(path.join(__dirname, '../data/chinook-mysql.sql')));
}

View File

@@ -7,6 +7,8 @@ const dbgatePluginMysql = require('dbgate-plugin-mysql');
dbgateApi.registerPlugins(dbgatePluginMysql);
const dbgatePluginPostgres = require('dbgate-plugin-postgres');
dbgateApi.registerPlugins(dbgatePluginPostgres);
const dbgatePluginDynamodb = require('dbgate-plugin-dynamodb');
dbgateApi.registerPlugins(dbgatePluginDynamodb);
async function createDb(connection, dropDbSql, createDbSql, database = 'my_guitar_shop', { dropDatabaseName } = {}) {
if (dropDbSql) {
@@ -125,6 +127,28 @@ async function run() {
{ dropDatabaseName: 'my_guitar_shop' }
);
}
if (localconfig.dynamo) {
const dynamodbConnection = {
server: process.env.SERVER_dynamo,
port: process.env.PORT_dynamo,
authType: 'onpremise',
engine: 'dynamodb@dbgate-plugin-dynamodb',
};
const driver = dbgatePluginDynamodb.drivers.find(d => d.engine === 'dynamodb@dbgate-plugin-dynamodb');
const pool = await driver.connect(dynamodbConnection);
const collections = await driver.listCollections(pool);
for (const collection of collections) {
await driver.dropTable(pool, collection);
}
await driver.disconnect(pool);
await dbgateApi.importDbFromFolder({
connection: dynamodbConnection,
folder: path.resolve(path.join(__dirname, '../data/my-guitar-shop')),
});
}
}
dbgateApi.runScript(run);

55
e2e-tests/init/redis.js Normal file
View File

@@ -0,0 +1,55 @@
const path = require('path');
const fs = require('fs');
const dbgateApi = require('dbgate-api');
dbgateApi.initializeApiEnvironment();
const dbgatePluginRedis = require('dbgate-plugin-redis');
dbgateApi.registerPlugins(dbgatePluginRedis);
async function initRedisDatabase() {
await dbgateApi.executeQuery({
connection: {
server: process.env.SERVER_redis,
user: process.env.USER_redis,
password: process.env.PASSWORD_redis,
port: process.env.PORT_redis,
engine: 'redis@dbgate-plugin-redis',
},
sql: 'FLUSHALL',
});
const files = [
{
file: path.resolve(__dirname, '../data/redis-db1.redis'),
database: 0,
},
{
file: path.resolve(__dirname, '../data/redis-db2.redis'),
database: 1,
},
];
for (const { file, database } of files) {
await dbgateApi.executeQuery({
connection: {
server: process.env.SERVER_redis,
user: process.env.USER_redis,
password: process.env.PASSWORD_redis,
port: process.env.PORT_redis,
engine: 'redis@dbgate-plugin-redis',
database,
},
sqlFile: file,
});
}
}
async function run() {
await initRedisDatabase();
}
dbgateApi.runScript(run);
module.exports = {
initRedisDatabase,
};

133
e2e-tests/init/rest.js Normal file
View File

@@ -0,0 +1,133 @@
const fs = require('fs');
const path = require('path');
const { spawn, spawnSync } = require('child_process');
const rootDir = path.resolve(__dirname, '..', '..');
const testApiDir = path.join(rootDir, 'test-api');
const pidFile = path.resolve(__dirname, '..', 'tmpdata', 'test-api.pid');
const isWindows = process.platform === 'win32';
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function waitForApiReady(timeoutMs = 30000) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
try {
const response = await fetch('http://localhost:4444/openapi.json');
if (response.ok) {
return;
}
} catch (err) {
// continue waiting
}
await delay(500);
}
throw new Error('DBGM-00306 test-api did not start on port 4444 in time');
}
function readProcessStartTime(pid) {
if (process.platform === 'linux') {
try {
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
return stat.split(' ')[21] || null;
} catch (err) {
return null;
}
}
return null;
}
function isPidStillOurs(meta) {
if (!meta || !(meta.pid > 0)) return false;
if (process.platform === 'linux' && meta.startTime) {
const current = readProcessStartTime(meta.pid);
return current === meta.startTime;
}
return true;
}
function stopPreviousTestApi() {
if (!fs.existsSync(pidFile)) {
return;
}
try {
const content = fs.readFileSync(pidFile, 'utf-8').trim();
let meta;
try {
meta = JSON.parse(content);
} catch (_) {
const pid = Number(content);
meta = Number.isInteger(pid) && pid > 0 ? { pid } : null;
}
if (isPidStillOurs(meta)) {
process.kill(meta.pid);
}
} catch (err) {
// ignore stale pid file or already terminated process
}
try {
fs.unlinkSync(pidFile);
} catch (err) {
// ignore
}
}
function startTestApi() {
const command = isWindows ? 'cmd.exe' : 'yarn';
const args = isWindows ? ['/c', 'yarn start'] : ['start'];
const child = spawn(command, args, {
cwd: testApiDir,
env: {
...process.env,
PORT: '4444',
},
detached: true,
stdio: 'ignore',
});
child.unref();
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
const meta = { pid: child.pid };
const startTime = readProcessStartTime(child.pid);
if (startTime) meta.startTime = startTime;
fs.writeFileSync(pidFile, JSON.stringify(meta));
}
function ensureTestApiDependencies() {
const dependencyCheckFile = path.join(testApiDir, 'node_modules', 'swagger-jsdoc', 'package.json');
if (fs.existsSync(dependencyCheckFile)) {
return;
}
const installCommand = isWindows ? 'cmd.exe' : 'yarn';
const installArgs = isWindows ? ['/c', 'yarn install --silent'] : ['install', '--silent'];
const result = spawnSync(installCommand, installArgs, {
cwd: testApiDir,
stdio: 'inherit',
env: process.env,
});
if (result.status !== 0) {
throw new Error('DBGM-00307 Failed to install test-api dependencies');
}
}
async function run() {
stopPreviousTestApi();
ensureTestApiDependencies();
startTestApi();
await waitForApiReady();
}
run().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -10,39 +10,45 @@
"cypress-real-events": "^1.13.0",
"env-cmd": "^10.1.0",
"kill-port": "^2.0.1",
"mocha-reporter-gha": "^1.1.1",
"start-server-and-test": "^2.0.8"
},
"scripts": {
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
"cy:run:add-connection": "cypress run --spec cypress/e2e/add-connection.cy.js",
"cy:run:portal": "cypress run --spec cypress/e2e/portal.cy.js",
"cy:run:oauth": "cypress run --spec cypress/e2e/oauth.cy.js",
"cy:run:browse-data": "cypress run --spec cypress/e2e/browse-data.cy.js",
"cy:run:rest": "cypress run --spec cypress/e2e/rest.cy.js",
"cy:run:team": "cypress run --spec cypress/e2e/team.cy.js",
"cy:run:multi-sql": "cypress run --spec cypress/e2e/multi-sql.cy.js",
"cy:run:cloud": "cypress run --spec cypress/e2e/cloud.cy.js",
"cy:run:charts": "cypress run --spec cypress/e2e/charts.cy.js",
"cy:run:redis": "cypress run --spec cypress/e2e/redis.cy.js",
"cy:run:ai-chat": "cypress run --spec cypress/e2e/ai-chat.cy.js",
"start:add-connection": "node clearTestingData && cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:portal": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/portal/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:oauth": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/oauth/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:browse-data": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/browse-data/.env node e2e-tests/init/browse-data.js && env-cmd -f e2e-tests/env/browse-data/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:rest": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/rest/.env node e2e-tests/init/rest.js && env-cmd -f e2e-tests/env/rest/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:team": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/team/.env node e2e-tests/init/team.js && env-cmd -f e2e-tests/env/team/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:multi-sql": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/multi-sql/.env node e2e-tests/init/multi-sql.js && env-cmd -f e2e-tests/env/multi-sql/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:cloud": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/cloud/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:charts": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/charts/.env node e2e-tests/init/charts.js && env-cmd -f e2e-tests/env/charts/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:redis": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/redis/.env node e2e-tests/init/redis.js && env-cmd -f e2e-tests/env/redis/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:ai-chat": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/ai-chat/.env node e2e-tests/init/ai-chat.js && env-cmd -f e2e-tests/env/ai-chat/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"test:add-connection": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection",
"test:portal": "start-server-and-test start:portal http://localhost:3000 cy:run:portal",
"test:oauth": "start-server-and-test start:oauth http://localhost:3000 cy:run:oauth",
"test:browse-data": "start-server-and-test start:browse-data http://localhost:3000 cy:run:browse-data",
"test:rest": "start-server-and-test start:rest http://localhost:3000 cy:run:rest",
"test:team": "start-server-and-test start:team http://localhost:3000 cy:run:team",
"test:multi-sql": "start-server-and-test start:multi-sql http://localhost:3000 cy:run:multi-sql",
"test:cloud": "start-server-and-test start:cloud http://localhost:3000 cy:run:cloud",
"test:charts": "start-server-and-test start:charts http://localhost:3000 cy:run:charts",
"test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team && yarn test:multi-sql && yarn test:cloud && yarn test:charts",
"test:redis": "start-server-and-test start:redis http://localhost:3000 cy:run:redis",
"test:ai-chat": "start-server-and-test start:ai-chat http://localhost:3000 cy:run:ai-chat",
"test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:rest && yarn test:team && yarn test:multi-sql && yarn test:cloud && yarn test:charts && yarn test:redis && yarn test:ai-chat",
"test:ci": "yarn test"
},
"dependencies": {}

2
e2e-tests/tmpdata/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
test-api.pid
aigwmock.pid

View File

@@ -2,6 +2,34 @@
# yarn lockfile v1
"@actions/core@^1.10.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.11.1.tgz#ae683aac5112438021588030efb53b1adb86f172"
integrity sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==
dependencies:
"@actions/exec" "^1.1.1"
"@actions/http-client" "^2.0.1"
"@actions/exec@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.1.1.tgz#2e43f28c54022537172819a7cf886c844221a611"
integrity sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==
dependencies:
"@actions/io" "^1.0.1"
"@actions/http-client@^2.0.1":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674"
integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==
dependencies:
tunnel "^0.0.6"
undici "^5.25.4"
"@actions/io@^1.0.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71"
integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -39,6 +67,11 @@
debug "^3.1.0"
lodash.once "^4.1.1"
"@fastify/busboy@^2.0.0":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
@@ -947,6 +980,13 @@ minimist@^1.2.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mocha-reporter-gha@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/mocha-reporter-gha/-/mocha-reporter-gha-1.1.1.tgz#e1248abd0769f55b57b36ccd7db2b0b6573d5adf"
integrity sha512-CFbcgM56V4yWlbF91XuwrE6a5X/IqjVXTPefO7m8cY8Es8G1UhJ2KKOrk16AcSemRzVWXp2Fdy3bWJ7j45snWw==
dependencies:
"@actions/core" "^1.10.1"
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -1292,6 +1332,11 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -1307,6 +1352,13 @@ undici-types@~6.20.0:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
undici@^5.25.4:
version "5.29.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3"
integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==
dependencies:
"@fastify/busboy" "^2.0.0"
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"

View File

@@ -12,6 +12,7 @@ const {
} = require('dbgate-tools');
function pickImportantTableInfo(engine, table) {
if (!table) return table;
const props = ['columnName', 'defaultValue'];
if (!engine.skipNullability) props.push('notNull');
if (!engine.skipAutoIncrement) props.push('autoIncrement');
@@ -25,6 +26,15 @@ function pickImportantTableInfo(engine, table) {
.map(props =>
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
),
// TODO:
foreignKeys: table.foreignKeys
.sort((a, b) => a.refTableName.localeCompare(b.refTableName))
.map(fk => ({
constraintType: fk.constraintType,
refTableName: fk.refTableName,
columns: fk.columns.map(col => ({ columnName: col.columnName, refColumnName: col.refColumnName })),
})),
};
}
@@ -33,7 +43,7 @@ function checkTableStructure(engine, t1, t2) {
expect(pickImportantTableInfo(engine, t1)).toEqual(pickImportantTableInfo(engine, t2));
}
async function testTableDiff(engine, conn, driver, mangle) {
async function testTableDiff(engine, conn, driver, mangle, changedTable = 't1') {
const initQuery = formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`);
await driver.query(conn, transformSqlForEngine(engine, initQuery));
@@ -68,17 +78,39 @@ async function testTableDiff(engine, conn, driver, mangle) {
await driver.query(conn, transformSqlForEngine(engine, query));
}
const tget = x => x.tables.find(y => y.pureName == 't1');
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
if (!engine.skipReferences) {
const query = formatQueryWithoutParams(
driver,
`create table ~t3 (~id int not null primary key, ~fkval int ${
driver.dialect.implicitNullDeclaration ? '' : 'null'
})`
);
await driver.query(conn, transformSqlForEngine(engine, query));
}
const tget = x => x?.tables?.find(y => y.pureName == changedTable);
const structure1Source = await driver.analyseFull(conn);
const structure1 = generateDbPairingId(extendDatabaseInfo(structure1Source));
let structure2 = _.cloneDeep(structure1);
mangle(tget(structure2));
structure2 = extendDatabaseInfo(structure2);
const { sql } = getAlterTableScript(tget(structure1), tget(structure2), {}, structure1, structure2, driver);
// sleep 1s - some engines have update datetime precision only to seconds
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('RUNNING ALTER SQL', driver.engine, ':', sql);
await driver.script(conn, sql);
// TODO:
// if (!engine.skipIncrementalAnalysis) {
// const structure2RealIncremental = await driver.analyseIncremental(conn, structure1Source);
// checkTableStructure(engine, tget(structure2RealIncremental), tget(structure2));
// }
const structure2Real = extendDatabaseInfo(await driver.analyseFull(conn));
checkTableStructure(engine, tget(structure2Real), tget(structure2));
@@ -87,6 +119,7 @@ async function testTableDiff(engine, conn, driver, mangle) {
const TESTED_COLUMNS = ['col_pk', 'col_std', 'col_def', 'col_fk', 'col_ref', 'col_idx', 'col_uq'];
// const TESTED_COLUMNS = ['col_pk'];
// const TESTED_COLUMNS = ['col_fk'];
// const TESTED_COLUMNS = ['col_idx'];
// const TESTED_COLUMNS = ['col_def'];
// const TESTED_COLUMNS = ['col_std'];
@@ -150,11 +183,25 @@ describe('Alter table', () => {
)(
'Drop column - %s - %s',
testWrapper(async (conn, driver, column, engine) => {
await testTableDiff(engine, conn, driver, tbl => (tbl.columns = tbl.columns.filter(x => x.columnName != column)));
await testTableDiff(engine, conn, driver,
tbl => {
tbl.columns = tbl.columns.filter(x => x.columnName != column);
tbl.foreignKeys = tbl.foreignKeys
.map(fk => ({
...fk,
columns: fk.columns.filter(col => col.columnName != column)
}))
.filter(fk => fk.columns.length > 0);
}
);
})
);
test.each(createEnginesColumnsSource(engines.filter(x => !x.skipNullable && !x.skipChangeNullability)))(
test.each(
createEnginesColumnsSource(engines.filter(x => !x.skipNullability && !x.skipChangeNullability)).filter(
([_label, col]) => !col.endsWith('_pk')
)
)(
'Change nullability - %s - %s',
testWrapper(async (conn, driver, column, engine) => {
await testTableDiff(
@@ -173,7 +220,11 @@ describe('Alter table', () => {
engine,
conn,
driver,
tbl => (tbl.columns = tbl.columns.map(x => (x.columnName == column ? { ...x, columnName: 'col_renamed' } : x)))
tbl => {
tbl.columns = tbl.columns.map(x => (x.columnName == column ? { ...x, columnName: 'col_renamed' } : x));
tbl.foreignKeys = tbl.foreignKeys.map(fk => ({...fk, columns: fk.columns.map(col => col.columnName == column ? { ...col, columnName: 'col_renamed' } : col)
}));
}
);
})
);
@@ -214,6 +265,48 @@ describe('Alter table', () => {
})
);
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
'Drop FK - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(
engine,
conn,
driver,
tbl => {
tbl.foreignKeys = [];
},
't2'
);
})
);
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
'Create FK - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(
engine,
conn,
driver,
tbl => {
tbl.foreignKeys = [
{
constraintType: 'foreignKey',
pureName: 't3',
refTableName: 't1',
columns: [
{
columnName: 'fkval',
refColumnName: 'col_ref',
},
],
},
];
},
't3'
);
})
);
// test.each(engines.map(engine => [engine.label, engine]))(
// 'Change autoincrement - %s',
// testWrapper(async (conn, driver, engine) => {

View File

@@ -0,0 +1,536 @@
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
const crypto = require('crypto');
const stream = require('stream');
const { mongoDbEngine, dynamoDbEngine } = require('../engines');
const tableWriter = require('dbgate-api/src/shell/tableWriter');
const tableReader = require('dbgate-api/src/shell/tableReader');
const copyStream = require('dbgate-api/src/shell/copyStream');
function randomCollectionName() {
return 'test_' + crypto.randomBytes(6).toString('hex');
}
const documentEngines = [
{ label: 'MongoDB', engine: mongoDbEngine },
{ label: 'DynamoDB', engine: dynamoDbEngine },
];
async function connectEngine(engine) {
const driver = requireEngineDriver(engine.connection);
const conn = await driver.connect(engine.connection);
return { driver, conn };
}
async function createCollection(driver, conn, collectionName, engine) {
if (engine.connection.engine.startsWith('dynamodb')) {
await driver.operation(conn, {
type: 'createCollection',
collection: {
name: collectionName,
partitionKey: '_id',
partitionKeyType: 'S',
},
});
} else {
await driver.operation(conn, {
type: 'createCollection',
collection: { name: collectionName },
});
}
}
async function dropCollection(driver, conn, collectionName) {
try {
await driver.operation(conn, {
type: 'dropCollection',
collection: collectionName,
});
} catch (e) {
// Ignore errors when dropping (collection may not exist)
}
}
async function insertDocument(driver, conn, collectionName, doc) {
return driver.updateCollection(conn, {
inserts: [{ pureName: collectionName, document: {}, fields: doc }],
updates: [],
deletes: [],
});
}
async function readAll(driver, conn, collectionName) {
return driver.readCollection(conn, { pureName: collectionName, limit: 1000 });
}
async function updateDocument(driver, conn, collectionName, condition, fields) {
return driver.updateCollection(conn, {
inserts: [],
updates: [{ pureName: collectionName, condition, fields }],
deletes: [],
});
}
async function deleteDocument(driver, conn, collectionName, condition) {
return driver.updateCollection(conn, {
inserts: [],
updates: [],
deletes: [{ pureName: collectionName, condition }],
});
}
describe('Collection CRUD', () => {
describe.each(documentEngines.map(e => [e.label, e.engine]))('%s', (label, engine) => {
let driver;
let conn;
let collectionName;
beforeAll(async () => {
const result = await connectEngine(engine);
driver = result.driver;
conn = result.conn;
});
afterAll(async () => {
if (conn) {
await driver.close(conn);
}
});
beforeEach(async () => {
collectionName = randomCollectionName();
await createCollection(driver, conn, collectionName, engine);
});
afterEach(async () => {
await dropCollection(driver, conn, collectionName);
});
// ---- INSERT ----
test('insert a single document', async () => {
const res = await insertDocument(driver, conn, collectionName, {
_id: 'doc1',
name: 'Alice',
age: 30,
});
expect(res.inserted.length).toBe(1);
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Alice');
expect(all.rows[0].age).toBe(30);
});
test('insert multiple documents', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'a1', name: 'Alice' });
await insertDocument(driver, conn, collectionName, { _id: 'a2', name: 'Bob' });
await insertDocument(driver, conn, collectionName, { _id: 'a3', name: 'Charlie' });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(3);
const names = all.rows.map(r => r.name).sort();
expect(names).toEqual(['Alice', 'Bob', 'Charlie']);
});
test('insert document with nested object', async () => {
await insertDocument(driver, conn, collectionName, {
_id: 'nested1',
name: 'Alice',
address: { city: 'Prague', zip: '11000' },
});
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].address.city).toBe('Prague');
expect(all.rows[0].address.zip).toBe('11000');
});
// ---- READ ----
test('read from empty collection returns no rows', async () => {
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(0);
});
test('read with limit', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'l1', name: 'A' });
await insertDocument(driver, conn, collectionName, { _id: 'l2', name: 'B' });
await insertDocument(driver, conn, collectionName, { _id: 'l3', name: 'C' });
const limited = await driver.readCollection(conn, {
pureName: collectionName,
limit: 2,
});
expect(limited.rows.length).toBe(2);
});
test('count documents', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'c1', name: 'A' });
await insertDocument(driver, conn, collectionName, { _id: 'c2', name: 'B' });
const result = await driver.readCollection(conn, {
pureName: collectionName,
countDocuments: true,
});
expect(result.count).toBe(2);
});
test('count documents on empty collection returns zero', async () => {
const result = await driver.readCollection(conn, {
pureName: collectionName,
countDocuments: true,
});
expect(result.count).toBe(0);
});
// ---- UPDATE ----
test('update an existing document', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'u1', name: 'Alice', age: 25 });
const res = await updateDocument(driver, conn, collectionName, { _id: 'u1' }, { name: 'Alice Updated' });
expect(res.errorMessage).toBeUndefined();
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Alice Updated');
});
test('update does not create new document', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'u2', name: 'Bob' });
await updateDocument(driver, conn, collectionName, { _id: 'nonexistent' }, { name: 'Ghost' });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Bob');
});
test('update only specified fields', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'u3', name: 'Carol', age: 40, city: 'London' });
await updateDocument(driver, conn, collectionName, { _id: 'u3' }, { age: 41 });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Carol');
expect(all.rows[0].age).toBe(41);
expect(all.rows[0].city).toBe('London');
});
// ---- DELETE ----
test('delete an existing document', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'd1', name: 'Alice' });
await insertDocument(driver, conn, collectionName, { _id: 'd2', name: 'Bob' });
const res = await deleteDocument(driver, conn, collectionName, { _id: 'd1' });
expect(res.errorMessage).toBeUndefined();
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Bob');
});
test('delete non-existing document does not affect collection', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'dx1', name: 'Alice' });
await deleteDocument(driver, conn, collectionName, { _id: 'nonexistent' });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Alice');
});
test('delete all documents leaves empty collection', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'da1', name: 'A' });
await insertDocument(driver, conn, collectionName, { _id: 'da2', name: 'B' });
await deleteDocument(driver, conn, collectionName, { _id: 'da1' });
await deleteDocument(driver, conn, collectionName, { _id: 'da2' });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(0);
});
// ---- EDGE CASES ----
test('insert and read document with empty string field', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'e1', name: '', value: 'test' });
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('');
expect(all.rows[0].value).toBe('test');
});
test('insert and read document with numeric values', async () => {
await insertDocument(driver, conn, collectionName, {
_id: 'n1',
intVal: 42,
floatVal: 3.14,
zero: 0,
negative: -10,
});
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].intVal).toBe(42);
expect(all.rows[0].floatVal).toBeCloseTo(3.14);
expect(all.rows[0].zero).toBe(0);
expect(all.rows[0].negative).toBe(-10);
});
test('insert and read document with boolean values', async () => {
await insertDocument(driver, conn, collectionName, {
_id: 'b1',
active: true,
deleted: false,
});
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].active).toBe(true);
expect(all.rows[0].deleted).toBe(false);
});
test('reading non-existing collection returns error or empty', async () => {
const result = await driver.readCollection(conn, {
pureName: 'nonexistent_collection_' + crypto.randomBytes(4).toString('hex'),
limit: 10,
});
// Depending on the driver, this may return an error or empty rows
if (result.errorMessage) {
expect(typeof result.errorMessage).toBe('string');
} else {
expect(result.rows.length).toBe(0);
}
});
test('replace full document via update with document field', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'r1', name: 'Original', extra: 'data' });
await driver.updateCollection(conn, {
inserts: [],
updates: [
{
pureName: collectionName,
condition: { _id: 'r1' },
document: { _id: 'r1', name: 'Replaced' },
fields: {},
},
],
deletes: [],
});
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].name).toBe('Replaced');
});
test('insert then update then delete lifecycle', async () => {
// Insert
await insertDocument(driver, conn, collectionName, { _id: 'life1', name: 'Lifecycle', status: 'created' });
let all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(1);
expect(all.rows[0].status).toBe('created');
// Update
await updateDocument(driver, conn, collectionName, { _id: 'life1' }, { status: 'updated' });
all = await readAll(driver, conn, collectionName);
expect(all.rows[0].status).toBe('updated');
// Delete
await deleteDocument(driver, conn, collectionName, { _id: 'life1' });
all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(0);
});
});
});
function createDocumentImportStream(documents) {
const pass = new stream.PassThrough({ objectMode: true });
pass.write({ __isStreamHeader: true, __isDynamicStructure: true });
for (const doc of documents) {
pass.write(doc);
}
pass.end();
return pass;
}
function createExportStream() {
const writable = new stream.Writable({ objectMode: true });
writable.resultArray = [];
writable._write = (chunk, encoding, callback) => {
writable.resultArray.push(chunk);
callback();
};
return writable;
}
describe('Collection Import/Export', () => {
describe.each(documentEngines.map(e => [e.label, e.engine]))('%s', (label, engine) => {
let driver;
let conn;
let collectionName;
beforeAll(async () => {
const result = await connectEngine(engine);
driver = result.driver;
conn = result.conn;
});
afterAll(async () => {
if (conn) {
await driver.close(conn);
}
});
beforeEach(async () => {
collectionName = randomCollectionName();
await createCollection(driver, conn, collectionName, engine);
});
afterEach(async () => {
await dropCollection(driver, conn, collectionName);
});
test('import documents via stream', async () => {
const documents = [
{ _id: 'imp1', name: 'Alice', age: 30 },
{ _id: 'imp2', name: 'Bob', age: 25 },
{ _id: 'imp3', name: 'Charlie', age: 35 },
];
const reader = createDocumentImportStream(documents);
const writer = await tableWriter({
systemConnection: conn,
driver,
pureName: collectionName,
createIfNotExists: true,
});
await copyStream(reader, writer);
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(3);
const names = all.rows.map(r => r.name).sort();
expect(names).toEqual(['Alice', 'Bob', 'Charlie']);
});
test('export documents via stream', async () => {
await insertDocument(driver, conn, collectionName, { _id: 'exp1', name: 'Alice', city: 'Prague' });
await insertDocument(driver, conn, collectionName, { _id: 'exp2', name: 'Bob', city: 'Vienna' });
await insertDocument(driver, conn, collectionName, { _id: 'exp3', name: 'Charlie', city: 'Berlin' });
const reader = await tableReader({
systemConnection: conn,
driver,
pureName: collectionName,
});
const writer = createExportStream();
await copyStream(reader, writer);
const rows = writer.resultArray.filter(x => !x.__isStreamHeader);
expect(rows.length).toBe(3);
const names = rows.map(r => r.name).sort();
expect(names).toEqual(['Alice', 'Bob', 'Charlie']);
});
test('import then export round-trip', async () => {
const documents = [
{ _id: 'rt1', name: 'Alice', value: 100 },
{ _id: 'rt2', name: 'Bob', value: 200 },
{ _id: 'rt3', name: 'Charlie', value: 300 },
{ _id: 'rt4', name: 'Diana', value: 400 },
];
// Import
const importReader = createDocumentImportStream(documents);
const importWriter = await tableWriter({
systemConnection: conn,
driver,
pureName: collectionName,
createIfNotExists: true,
});
await copyStream(importReader, importWriter);
// Export
const exportReader = await tableReader({
systemConnection: conn,
driver,
pureName: collectionName,
});
const exportWriter = createExportStream();
await copyStream(exportReader, exportWriter);
const rows = exportWriter.resultArray.filter(x => !x.__isStreamHeader);
expect(rows.length).toBe(4);
const sortedRows = rows.sort((a, b) => a._id.localeCompare(b._id));
for (const doc of documents) {
const found = sortedRows.find(r => r._id === doc._id);
expect(found).toBeDefined();
expect(found.name).toBe(doc.name);
expect(found.value).toBe(doc.value);
}
});
test('import documents with nested objects', async () => {
const documents = [
{ _id: 'nest1', name: 'Alice', address: { city: 'Prague', zip: '11000' } },
{ _id: 'nest2', name: 'Bob', address: { city: 'Vienna', zip: '1010' } },
];
const reader = createDocumentImportStream(documents);
const writer = await tableWriter({
systemConnection: conn,
driver,
pureName: collectionName,
createIfNotExists: true,
});
await copyStream(reader, writer);
const all = await readAll(driver, conn, collectionName);
expect(all.rows.length).toBe(2);
const alice = all.rows.find(r => r.name === 'Alice');
expect(alice.address.city).toBe('Prague');
expect(alice.address.zip).toBe('11000');
});
test('import many documents', async () => {
const documents = [];
for (let i = 0; i < 150; i++) {
documents.push({ _id: `many${i}`, name: `Name${i}`, index: i });
}
const reader = createDocumentImportStream(documents);
const writer = await tableWriter({
systemConnection: conn,
driver,
pureName: collectionName,
createIfNotExists: true,
});
await copyStream(reader, writer);
const result = await driver.readCollection(conn, {
pureName: collectionName,
countDocuments: true,
});
expect(result.count).toBe(150);
});
test('export empty collection returns no data rows', async () => {
const reader = await tableReader({
systemConnection: conn,
driver,
pureName: collectionName,
});
const writer = createExportStream();
await copyStream(reader, writer);
const rows = writer.resultArray.filter(x => !x.__isStreamHeader);
expect(rows.length).toBe(0);
});
});
});

View File

@@ -303,4 +303,52 @@ describe('Data replicator', () => {
}),
15 * 1000
);
test.each(engines.filter(x => !x.skipDataReplicator).map(engine => [engine.label, engine]))(
'Skip columns for update - %s',
testWrapper(async (conn, driver, engine) => {
runCommandOnDriver(conn, driver, dmp =>
dmp.createTable({
pureName: 't1',
columns: [
{ columnName: 'id', dataType: 'int', autoIncrement: true, notNull: true },
{ columnName: 'key', dataType: 'varchar(50)', notNull: true },
{ columnName: 'val', dataType: 'varchar(50)' },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
})
);
const getcfg = (v1 = 'v1') => ({
systemConnection: conn,
driver,
items: [
{
name: 't1',
matchColumns: ['key'],
skipUpdateColumns: ['val'],
findExisting: true,
updateExisting: true,
createNew: true,
jsonArray: [
{ key: '1', val: v1 },
{ key: '2', val: 'v2' },
{ key: '3', val: 'v3' },
],
},
],
});
await dataReplicator(getcfg('v1'));
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select ~val from ~t1 where ~key='1'`));
expect(res1.rows[0].val).toEqual('v1');
await dataReplicator(getcfg('v2'));
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select ~val from ~t1 where ~key='1'`));
expect(res2.rows[0].val).toEqual('v1');
})
);
});

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);
});
})
);
});

View File

@@ -33,7 +33,9 @@ describe('Schema tests', () => {
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeTruthy();
expect(schemas2.length).toEqual(count + 1);
expect(schemas2.find(x => x.isDefault).schemaName).toEqual(engine.defaultSchemaName);
expect(structure2).toBeNull();
if (!engine.skipIncrementalAnalysis) {
expect(structure2).toBeNull();
}
})
);
@@ -51,7 +53,9 @@ describe('Schema tests', () => {
const structure2 = await driver.analyseIncremental(conn, structure1);
const schemas2 = await driver.listSchemas(conn);
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy();
expect(structure2).toBeNull();
if (!engine.skipIncrementalAnalysis) {
expect(structure2).toBeNull();
}
})
);

View File

@@ -94,7 +94,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
test.each(engines.map(engine => [engine.label, engine]))(
'Table add - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
@@ -112,7 +112,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
test.each(engines.map(engine => [engine.label, engine]))(
'Table remove - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));
@@ -130,7 +130,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
test.each(engines.map(engine => [engine.label, engine]))(
'Table change - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));

View File

@@ -44,7 +44,7 @@ services:
# - 15942:9042
#
# clickhouse:
# image: bitnami/clickhouse:24.8.4
# image: bitnamilegacy/clickhouse:24.8.4
# restart: always
# ports:
# - 15005:8123
@@ -123,5 +123,22 @@ services:
retries: 3
start_period: 40s
mongodb:
image: mongo:4.0.12
restart: always
volumes:
- mongo-data:/data/db
- mongo-config:/data/configdb
ports:
- 27017:27017
dynamodb:
image: amazon/dynamodb-local
restart: always
ports:
- 8000:8000
volumes:
firebird-data:
mongo-data:
mongo-config:

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',
@@ -186,6 +187,7 @@ const mariaDbEngine = {
/** @type {import('dbgate-types').TestEngineInfo} */
const postgreSqlEngine = {
label: 'PostgreSQL',
skipIncrementalAnalysis: true,
connection: {
engine: 'postgres@dbgate-plugin-postgres',
password: 'Pwd2020Db',
@@ -216,6 +218,7 @@ const postgreSqlEngine = {
supportSchemas: true,
supportRenameSqlObject: true,
defaultSchemaName: 'public',
binaryDataType: 'bytea',
dumpFile: 'data/chinook-postgre.sql',
dumpChecks: [
{
@@ -446,6 +449,7 @@ const sqlServerEngine = {
supportTableComments: true,
supportColumnComments: true,
// skipSeparateSchemas: true,
binaryDataType: 'varbinary(100)',
triggers: [
{
testName: 'triggers before each row',
@@ -506,6 +510,7 @@ const sqliteEngine = {
},
},
],
binaryDataType: 'blob',
};
const libsqlFileEngine = {
@@ -619,6 +624,7 @@ const oracleEngine = {
},
},
],
binaryDataType: 'blob',
};
/** @type {import('dbgate-types').TestEngineInfo} */
@@ -732,6 +738,27 @@ const firebirdEngine = {
skipDropReferences: true,
};
/** @type {import('dbgate-types').TestEngineInfo} */
const mongoDbEngine = {
label: 'MongoDB',
connection: {
engine: 'mongo@dbgate-plugin-mongo',
server: 'localhost',
port: 27017,
},
};
/** @type {import('dbgate-types').TestEngineInfo} */
const dynamoDbEngine = {
label: 'DynamoDB',
connection: {
engine: 'dynamodb@dbgate-plugin-dynamodb',
server: 'localhost',
port: 8000,
authType: 'onpremise',
},
};
const enginesOnCi = [
// all engines, which would be run on GitHub actions
mysqlEngine,
@@ -754,16 +781,16 @@ const enginesOnLocal = [
// cassandraEngine,
// mysqlEngine,
// mariaDbEngine,
// postgreSqlEngine,
// sqlServerEngine,
postgreSqlEngine,
//sqlServerEngine,
// sqliteEngine,
// cockroachDbEngine,
// clickhouseEngine,
// libsqlFileEngine,
// libsqlWsEngine,
// oracleEngine,
//oracleEngine,
// duckdbEngine,
firebirdEngine,
//firebirdEngine,
];
/** @type {import('dbgate-types').TestEngineInfo[] & Record<string, import('dbgate-types').TestEngineInfo>} */
@@ -782,3 +809,5 @@ module.exports.libsqlFileEngine = libsqlFileEngine;
module.exports.libsqlWsEngine = libsqlWsEngine;
module.exports.duckdbEngine = duckdbEngine;
module.exports.firebirdEngine = firebirdEngine;
module.exports.mongoDbEngine = mongoDbEngine;
module.exports.dynamoDbEngine = dynamoDbEngine;

View File

@@ -1,3 +1,4 @@
module.exports = {
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
reporters: ['default', 'github-actions'],
};

View File

@@ -1,7 +1,7 @@
{
"name": "dbgate-integration-tests",
"version": "6.0.0-alpha.1",
"homepage": "https://dbgate.org/",
"version": "7.0.0-alpha.1",
"homepage": "https://www.dbgate.io/",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
@@ -18,7 +18,7 @@
},
"devDependencies": {
"cross-env": "^7.0.3",
"jest": "^27.0.1",
"jest": "^28.1.3",
"pino-pretty": "^11.2.2",
"tmp": "^0.2.3"
}

View File

@@ -1,6 +1,7 @@
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
'dbgate-datalib': require('dbgate-datalib'),
};
const { prettyFactory } = require('pino-pretty');

View File

@@ -22,7 +22,9 @@ async function connect(engine, database) {
if (engine.generateDbFile) {
const conn = await driver.connect({
...connection,
databaseFile: (engine.databaseFileLocationOnServer ?? 'dbtemp/') + database,
databaseFile:
(engine.databaseFileLocationOnServer ?? (process.env.CITEST ? 'dbtemp/' : 'integration-tests/dbtemp/')) +
database,
});
return conn;
} else {

View File

@@ -1,14 +1,16 @@
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
const engines = require('./engines');
const { mongoDbEngine, dynamoDbEngine } = require('./engines');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
'dbgate-datalib': require('dbgate-datalib'),
};
async function connectEngine(engine) {
const { connection } = engine;
const driver = requireEngineDriver(connection);
for (;;) {
for (; ;) {
try {
const conn = await driver.connect(connection);
await driver.getVersion(conn);
@@ -25,7 +27,8 @@ async function connectEngine(engine) {
async function run() {
await new Promise(resolve => setTimeout(resolve, 10000));
await Promise.all(engines.map(engine => connectEngine(engine)));
const documentEngines = [mongoDbEngine, dynamoDbEngine];
await Promise.all([...engines, ...documentEngines].map(engine => connectEngine(engine)));
}
run();

View File

@@ -1,6 +1,6 @@
{
"private": true,
"version": "6.6.6-premium-beta.2",
"version": "7.1.8",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -22,6 +22,7 @@
"start:api:auth": "yarn workspace dbgate-api start:auth | pino-pretty",
"start:api:dblogin": "yarn workspace dbgate-api start:dblogin | pino-pretty",
"start:api:storage": "yarn workspace dbgate-api start:storage | pino-pretty",
"start:api:sfill": "yarn workspace dbgate-api start:sfill | pino-pretty",
"start:api:storage:built": "yarn workspace dbgate-api start:storage:built | pino-pretty",
"start:api:azure": "yarn workspace dbgate-api start:azure | pino-pretty",
"start:api:e2e:team": "yarn workspace dbgate-api start:e2e:team | pino-pretty",
@@ -29,13 +30,15 @@
"start:web": "yarn workspace dbgate-web dev",
"start:sqltree": "yarn workspace dbgate-sqltree start",
"start:tools": "yarn workspace dbgate-tools start",
"start:rest": "yarn workspace dbgate-rest start",
"start:datalib": "yarn workspace dbgate-datalib start",
"start:filterparser": "yarn workspace dbgate-filterparser start",
"build:sqltree": "yarn workspace dbgate-sqltree build",
"build:datalib": "yarn workspace dbgate-datalib build",
"build:filterparser": "yarn workspace dbgate-filterparser build",
"build:tools": "yarn workspace dbgate-tools build",
"build:lib": "yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib",
"build:rest": "yarn workspace dbgate-rest build",
"build:lib": "yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib && yarn build:rest",
"build:app": "yarn plugins:copydist && cd app && yarn install && yarn build",
"build:api": "yarn workspace dbgate-api build",
"build:api:doc": "yarn workspace dbgate-api build:doc",
@@ -52,6 +55,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",
@@ -61,7 +65,7 @@
"prepare:packer": "yarn plugins:copydist && yarn build:web && yarn build:api && yarn copy:packer:build",
"build:e2e": "yarn build:lib && yarn prepare:packer",
"start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"",
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn build:plugins:frontend:watch\"",
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn start:rest\" \"yarn build:plugins:frontend:watch\"",
"ts:api": "yarn workspace dbgate-api ts",
"ts:web": "yarn workspace dbgate-web ts",
"ts": "yarn ts:api && yarn ts:web",
@@ -73,6 +77,8 @@
"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:sort": "node common/translations-cli/index.js sort",
"translations:translate": "node common/translations-cli/translate.js",
"errors": "node common/assign-dbgm-codes.mjs ."
},
"dependencies": {

View File

@@ -0,0 +1,14 @@
{
"name": "dbgate-aigwmock",
"version": "1.0.0",
"description": "Mock AI Gateway server for E2E testing",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"license": "GPL-3.0",
"dependencies": {
"cors": "^2.8.6",
"express": "^5.2.1"
}
}

View File

@@ -0,0 +1,202 @@
const express = require('express');
const cors = require('cors');
const fs = require('fs');
const path = require('path');
const app = express();
app.use(cors());
app.use(express.json({ limit: '50mb' }));
const responses = JSON.parse(fs.readFileSync(path.join(__dirname, 'mockResponses.json'), 'utf-8'));
let callCounter = 0;
// GET /openrouter/v1/models
app.get('/openrouter/v1/models', (req, res) => {
res.json({
data: [{ id: 'mock-model', name: 'Mock Model' }],
preferredModel: 'mock-model',
});
});
// POST /openrouter/v1/chat/completions
app.post('/openrouter/v1/chat/completions', (req, res) => {
const messages = req.body.messages || [];
// Find the first user message (skip system messages)
const userMessage = messages.find(m => m.role === 'user');
if (!userMessage) {
return streamTextResponse(res, "I don't have enough context to help. Please ask a question.");
}
// Count assistant messages to determine the current step
const assistantCount = messages.filter(m => m.role === 'assistant').length;
// Find matching scenario by regex
const scenario = responses.scenarios.find(s => {
const regex = new RegExp(s.match, 'i');
return regex.test(userMessage.content);
});
if (!scenario) {
console.log(`[aigwmock] No scenario matched for: "${userMessage.content}"`);
return streamTextResponse(res, "I'm a mock AI assistant. I don't have a prepared response for that question.");
}
const step = scenario.steps[assistantCount];
if (!step) {
console.log(`[aigwmock] No more steps for scenario (step ${assistantCount})`);
return streamTextResponse(res, "I've completed my analysis of this topic.");
}
console.log(`[aigwmock] Scenario matched: "${scenario.match}", step ${assistantCount}, type: ${step.type}`);
if (step.type === 'tool_calls') {
return streamToolCallResponse(res, step.tool_calls);
} else {
return streamTextResponse(res, step.content);
}
});
function streamTextResponse(res, content) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
const id = `chatcmpl-mock-${Date.now()}`;
const created = Math.floor(Date.now() / 1000);
// Split content into chunks for realistic streaming
const chunkSize = 20;
const chunks = [];
for (let i = 0; i < content.length; i += chunkSize) {
chunks.push(content.substring(i, i + chunkSize));
}
// Send initial role chunk
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [{ index: 0, delta: { role: 'assistant', content: '' }, finish_reason: null }],
});
// Send content chunks
for (const chunk of chunks) {
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [{ index: 0, delta: { content: chunk }, finish_reason: null }],
});
}
// Send finish
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
});
res.write('data: [DONE]\n\n');
res.end();
}
function streamToolCallResponse(res, toolCalls) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
const id = `chatcmpl-mock-${Date.now()}`;
const created = Math.floor(Date.now() / 1000);
for (let i = 0; i < toolCalls.length; i++) {
const tc = toolCalls[i];
const callId = `call_mock_${++callCounter}`;
const args = JSON.stringify(tc.arguments);
if (i === 0) {
// First tool call: include role
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [
{
index: 0,
delta: {
role: 'assistant',
content: null,
tool_calls: [{ index: i, id: callId, type: 'function', function: { name: tc.name, arguments: '' } }],
},
finish_reason: null,
},
],
});
} else {
// Additional tool calls
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [
{
index: 0,
delta: {
tool_calls: [{ index: i, id: callId, type: 'function', function: { name: tc.name, arguments: '' } }],
},
finish_reason: null,
},
],
});
}
// Stream the arguments
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [
{
index: 0,
delta: {
tool_calls: [{ index: i, function: { arguments: args } }],
},
finish_reason: null,
},
],
});
}
// Send finish with tool_calls reason
writeSSE(res, {
id,
object: 'chat.completion.chunk',
created,
model: 'mock-model',
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
});
res.write('data: [DONE]\n\n');
res.end();
}
function writeSSE(res, data) {
res.write(`data: ${JSON.stringify(data)}\n\n`);
}
const port = process.env.PORT || 3110;
app.listen(port, () => {
console.log(`[aigwmock] AI Gateway mock server listening on port ${port}`);
});

View File

@@ -0,0 +1,193 @@
{
"scenarios": [
{
"match": "chart.*popular.*genre|popular.*genre.*chart|most popular genre",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Genre" } }
]
},
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Track" } }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_sql_select",
"arguments": {
"sql": "SELECT g.Name AS genre, COUNT(t.TrackId) AS track_count FROM Genre g JOIN Track t ON g.GenreId = t.GenreId GROUP BY g.Name ORDER BY track_count DESC LIMIT 10"
}
}
]
},
{
"type": "text",
"content": "Here is a chart showing the most popular genres by track count:\n\n```chart\n{\"type\":\"bar\",\"data\":{\"labels\":[\"Rock\",\"Latin\",\"Metal\",\"Alternative & Punk\",\"Jazz\",\"Blues\",\"Classical\",\"R&B/Soul\",\"Reggae\",\"Pop\"],\"datasets\":[{\"label\":\"Track Count\",\"data\":[1297,579,374,332,130,81,74,61,58,48]}]},\"options\":{\"plugins\":{\"title\":{\"display\":true,\"text\":\"Most Popular Genres by Track Count\"}}}}\n```"
}
]
},
{
"match": "most popular artist|popular artist|top artist",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Artist" } }
]
},
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Album" } }
]
},
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Track" } }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_sql_select",
"arguments": {
"sql": "SELECT ar.Name AS artist, COUNT(t.TrackId) AS track_count FROM Artist ar JOIN Album al ON ar.ArtistId = al.ArtistId JOIN Track t ON al.AlbumId = t.AlbumId GROUP BY ar.Name ORDER BY track_count DESC LIMIT 10"
}
}
]
},
{
"type": "text",
"content": "The most popular artist by number of tracks is **Iron Maiden** with 213 tracks, followed by **U2** with 135 tracks and **Led Zeppelin** with 114 tracks."
}
]
},
{
"match": "list.*user|show.*user|get.*user",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "graphql_introspect_schema", "arguments": {} }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_graphql_query",
"arguments": {
"query": "{ users { id firstName lastName email } }"
}
}
]
},
{
"type": "text",
"content": "Here are the users from the GraphQL API. The system contains multiple registered users with their names and email addresses."
}
]
},
{
"match": "chart.*product.*categor|product.*categor.*chart|chart.*categor",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "graphql_introspect_schema", "arguments": {} }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_graphql_query",
"arguments": {
"query": "{ products { category } }"
}
}
]
},
{
"type": "text",
"content": "Here is a bar chart showing the distribution of products across categories:\n\n```chart\n{\"type\":\"bar\",\"data\":{\"labels\":[\"Electronics\",\"Clothing\",\"Books\",\"Home & Garden\",\"Sports\",\"Toys\"],\"datasets\":[{\"label\":\"Number of Products\",\"data\":[35,30,33,38,32,32]}]},\"options\":{\"plugins\":{\"title\":{\"display\":true,\"text\":\"Products by Category\"}}}}\n```"
}
]
},
{
"match": "most expensive product|expensive.*product|highest price",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "graphql_introspect_schema", "arguments": {} }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_graphql_query",
"arguments": {
"query": "{ products { id name price category } }"
}
}
]
},
{
"type": "text",
"content": "Based on the query results, I found the most expensive product in the system. The product details are shown in the query results above."
}
]
},
{
"match": "show.*categor|list.*categor|all.*categor",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "graphql_introspect_schema", "arguments": {} }
]
},
{
"type": "tool_calls",
"tool_calls": [
{
"name": "execute_graphql_query",
"arguments": {
"query": "{ categories { id name description active } }"
}
}
]
},
{
"type": "text",
"content": "Here are all the categories available in the system. Each category has a name, description, and active status indicating whether it is currently in use."
}
]
},
{
"match": "Explain the following error|doesn't exist|does not exist",
"steps": [
{
"type": "tool_calls",
"tool_calls": [
{ "name": "get_table_schema", "arguments": { "table": "Invoice" } }
]
},
{
"type": "text",
"content": "The error occurs because the table `Invoice2` does not exist in the `MyChinook` database. The correct table name is `Invoice`. Here is the corrected query:\n\n```sql\nSELECT * FROM Invoice\n```\n\nThe table name had a typo — `Invoice2` instead of `Invoice`. The `Invoice` table contains columns like `InvoiceId`, `CustomerId`, `InvoiceDate`, `Total`, and billing address fields."
}
]
}
]
}

View File

@@ -2,7 +2,7 @@ DEVMODE=1
SHELL_SCRIPTING=1
ALLOW_DBGATE_PRIVATE_CLOUD=1
DEVWEB=1
LOCAL_AUTH_PROXY=1
# LOCAL_AUTH_PROXY=1
# LOCAL_AI_GATEWAY=true
# REDIRECT_TO_DBGATE_CLOUD_LOGIN=1

View File

@@ -1,6 +1,7 @@
DEVMODE=1
DEVWEB=1
CONNECTIONS=mysql,postgres,mongo,redis,mssql,oracle
CONNECTIONS=mysql,postgres,mongo,redis,mssql,oracle,mongourl
LABEL_mysql=MySql
SERVER_mysql=dbgatedckstage1.sprinx.cz
@@ -43,6 +44,10 @@ PORT_oracle=1521
ENGINE_oracle=oracle@dbgate-plugin-oracle
SERVICE_NAME_oracle=xe
LABEL_mongourl=Mongo URL
URL_mongourl=mongodb://root:Pwd2020Db@dbgatedckstage1.sprinx.cz:27017
ENGINE_mongourl=mongo@dbgate-plugin-mongo
# SETTINGS_dataGrid.showHintColumns=1
# docker run -p 3000:3000 -e CONNECTIONS=mongo -e URL_mongo=mongodb://localhost:27017 -e ENGINE_mongo=mongo@dbgate-plugin-mongo -e LABEL_mongo=mongo dbgate/dbgate:beta

54
packages/api/env/sfill/.env vendored Normal file
View File

@@ -0,0 +1,54 @@
DEVMODE=1
DEVWEB=1
# STORAGE_SERVER=localhost
# STORAGE_USER=root
# STORAGE_PASSWORD=Pwd2020Db
# STORAGE_PORT=3306
# STORAGE_DATABASE=dbgate-filled
# STORAGE_ENGINE=mysql@dbgate-plugin-mysql
STORAGE_SERVER=localhost
STORAGE_USER=postgres
STORAGE_PASSWORD=Pwd2020Db
STORAGE_PORT=5432
STORAGE_DATABASE=dbgate_sfill
STORAGE_ENGINE=postgres@dbgate-plugin-postgres
CONNECTIONS=mysql,postgres,mongo,redis
LABEL_mysql=MySql
SERVER_mysql=dbgatedckstage1.sprinx.cz
USER_mysql=root
PASSWORD_mysql=Pwd2020Db
PORT_mysql=3306
ENGINE_mysql=mysql@dbgate-plugin-mysql
LABEL_postgres=Postgres
SERVER_postgres=dbgatedckstage1.sprinx.cz
USER_postgres=postgres
PASSWORD_postgres=Pwd2020Db
PORT_postgres=5432
ENGINE_postgres=postgres@dbgate-plugin-postgres
LABEL_mongo=Mongo
SERVER_mongo=dbgatedckstage1.sprinx.cz
USER_mongo=root
PASSWORD_mongo=Pwd2020Db
PORT_mongo=27017
ENGINE_mongo=mongo@dbgate-plugin-mongo
LABEL_redis=Redis
SERVER_redis=dbgatedckstage1.sprinx.cz
ENGINE_redis=redis@dbgate-plugin-redis
PORT_redis=6379
ROLE_test1_CONNECTIONS=mysql
ROLE_test1_PERMISSIONS=widgets/*
ROLE_test1_DATABASES_db1_CONNECTION=mysql
ROLE_test1_DATABASES_db1_PERMISSION=run_script
ROLE_test1_DATABASES_db1_DATABASES=db1
ROLE_test1_DATABASES_db2_CONNECTION=redis
ROLE_test1_DATABASES_db2_PERMISSION=run_script
ROLE_test1_DATABASES_db2_DATABASES=db2

View File

@@ -1,8 +1,8 @@
{
"name": "dbgate-api",
"main": "src/index.js",
"version": "6.0.0-alpha.1",
"homepage": "https://dbgate.org/",
"version": "7.0.0-alpha.1",
"homepage": "https://www.dbgate.io/",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
@@ -24,16 +24,17 @@
"activedirectory2": "^2.1.0",
"archiver": "^7.0.1",
"async-lock": "^1.2.6",
"axios": "^0.21.1",
"axios": "^1.13.2",
"body-parser": "^1.19.0",
"byline": "^5.0.0",
"compare-versions": "^3.6.0",
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.7",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"dbgate-datalib": "^7.0.0-alpha.1",
"dbgate-query-splitter": "^4.12.0",
"dbgate-rest": "^7.0.0-alpha.1",
"dbgate-sqltree": "^7.0.0-alpha.1",
"dbgate-tools": "^7.0.0-alpha.1",
"debug": "^4.3.4",
"diff": "^5.0.0",
"diff2html": "^3.4.13",
@@ -75,6 +76,7 @@
"start:dblogin": "env-cmd -f env/dblogin/.env node src/index.js --listen-api",
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db --listen-api",
"start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
"start:sfill": "env-cmd -f env/sfill/.env node src/index.js --listen-api",
"start:storage:built": "env-cmd -f env/storage/.env cross-env DEVMODE= BUILTWEBMODE=1 node dist/bundle.js --listen-api",
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
"start:azure": "env-cmd -f env/azure/.env node src/index.js --listen-api",
@@ -86,7 +88,7 @@
"devDependencies": {
"@types/fs-extra": "^9.0.11",
"@types/lodash": "^4.14.149",
"dbgate-types": "^6.0.0-alpha.1",
"dbgate-types": "^7.0.0-alpha.1",
"env-cmd": "^10.1.0",
"jsdoc-to-markdown": "^9.0.5",
"node-loader": "^1.0.2",

View File

@@ -19,6 +19,26 @@ const unzipDirectory = require('../shell/unzipDirectory');
const logger = getLogger('archive');
/**
* Rejects any archive name (folder or file) that contains path-traversal
* sequences, directory separators, or null bytes. These values are used
* directly in path.join() calls; allowing traversal would let callers read
* or write arbitrary files outside the archive directory.
*/
function assertSafeArchiveName(name, label) {
if (typeof name !== 'string' || name.length === 0) {
throw new Error(`DBGM-00000 Invalid ${label}: must be a non-empty string`);
}
if (name.includes('\0') || name.includes('..') || name.includes('/') || name.includes('\\')) {
throw new Error(`DBGM-00000 Invalid ${label}: path traversal not allowed`);
}
// Reject names that resolve to the archive root itself (e.g. '.')
const resolved = path.resolve(archivedir(), name);
if (resolved === path.resolve(archivedir())) {
throw new Error(`DBGM-00000 Invalid ${label}: must not resolve to the archive root`);
}
}
module.exports = {
folders_meta: true,
async folders() {
@@ -39,6 +59,7 @@ module.exports = {
createFolder_meta: true,
async createFolder({ folder }) {
assertSafeArchiveName(folder, 'folder');
await fs.mkdir(path.join(archivedir(), folder));
socket.emitChanged('archive-folders-changed');
return true;
@@ -46,8 +67,12 @@ module.exports = {
createLink_meta: true,
async createLink({ linkedFolder }) {
if ( typeof linkedFolder !== 'string' || linkedFolder.length === 0) {
throw new Error(`DBGM-00000 Invalid linkedFolder: must be a non-empty string`);
}
assertSafeArchiveName(path.parse(linkedFolder).name, 'linkedFolder');
const folder = await this.getNewArchiveFolder({ database: path.parse(linkedFolder).name + '.link' });
fs.writeFile(path.join(archivedir(), folder), linkedFolder);
await fs.writeFile(path.join(archivedir(), folder), linkedFolder);
clearArchiveLinksCache();
socket.emitChanged('archive-folders-changed');
return folder;
@@ -71,6 +96,7 @@ module.exports = {
files_meta: true,
async files({ folder }) {
assertSafeArchiveName(folder, 'folder');
try {
if (folder.endsWith('.zip')) {
if (await fs.exists(path.join(archivedir(), folder))) {
@@ -121,6 +147,9 @@ module.exports = {
createFile_meta: true,
async createFile({ folder, file, fileType, tableInfo }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(fileType, 'fileType');
await fs.writeFile(
path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
tableInfo ? JSON.stringify({ __isStreamHeader: true, tableInfo }) : ''
@@ -131,6 +160,9 @@ module.exports = {
deleteFile_meta: true,
async deleteFile({ folder, file, fileType }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(fileType, 'fileType');
await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.${fileType}`));
socket.emitChanged(`archive-files-changed`, { folder });
return true;
@@ -138,6 +170,10 @@ module.exports = {
renameFile_meta: true,
async renameFile({ folder, file, newFile, fileType }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(newFile, 'newFile');
assertSafeArchiveName(fileType, 'fileType');
await fs.rename(
path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
path.join(resolveArchiveFolder(folder), `${newFile}.${fileType}`)
@@ -148,6 +184,8 @@ module.exports = {
modifyFile_meta: true,
async modifyFile({ folder, file, changeSet, mergedRows, mergeKey, mergeMode }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
await jsldata.closeDataStore(`archive://${folder}/${file}`);
const changedFilePath = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
@@ -187,6 +225,8 @@ module.exports = {
renameFolder_meta: true,
async renameFolder({ folder, newFolder }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(newFolder, 'newFolder');
const uniqueName = await this.getNewArchiveFolder({ database: newFolder });
await fs.rename(path.join(archivedir(), folder), path.join(archivedir(), uniqueName));
socket.emitChanged(`archive-folders-changed`);
@@ -196,6 +236,7 @@ module.exports = {
deleteFolder_meta: true,
async deleteFolder({ folder }) {
if (!folder) throw new Error('Missing folder parameter');
assertSafeArchiveName(folder, 'folder');
if (folder.endsWith('.link') || folder.endsWith('.zip')) {
await fs.unlink(path.join(archivedir(), folder));
} else {
@@ -207,6 +248,8 @@ module.exports = {
saveText_meta: true,
async saveText({ folder, file, text }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
socket.emitChanged(`archive-files-changed`, { folder });
return true;
@@ -214,6 +257,8 @@ module.exports = {
saveJslData_meta: true,
async saveJslData({ folder, file, jslid, changeSet }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
const source = getJslFileName(jslid);
const target = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
if (changeSet) {
@@ -232,11 +277,20 @@ module.exports = {
saveRows_meta: true,
async saveRows({ folder, file, rows }) {
const fileStream = fs.createWriteStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
const filePath = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
const fileStream = fs.createWriteStream(filePath);
for (const row of rows) {
await fileStream.write(JSON.stringify(row) + '\n');
const ok = fileStream.write(JSON.stringify(row) + '\n');
if (!ok) {
await new Promise(resolve => fileStream.once('drain', resolve));
}
}
await fileStream.close();
await new Promise((resolve, reject) => {
fileStream.end(() => resolve());
fileStream.on('error', reject);
});
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},
@@ -256,6 +310,8 @@ module.exports = {
getArchiveData_meta: true,
async getArchiveData({ folder, file }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
let rows;
if (folder.endsWith('.zip')) {
rows = await unzipJsonLinesFile(path.join(archivedir(), folder), `${file}.jsonl`);
@@ -270,7 +326,7 @@ module.exports = {
if (!fileName?.endsWith('.zip')) {
throw new Error(`${fileName} is not a ZIP file`);
}
assertSafeArchiveName(fileName.slice(0, -4), 'fileName');
const folder = await this.getNewArchiveFolder({ database: fileName });
await fs.copyFile(filePath, path.join(archivedir(), folder));
socket.emitChanged(`archive-folders-changed`);
@@ -280,6 +336,7 @@ module.exports = {
zip_meta: true,
async zip({ folder }) {
assertSafeArchiveName(folder, 'folder');
const newFolder = await this.getNewArchiveFolder({ database: folder + '.zip' });
await zipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));
socket.emitChanged(`archive-folders-changed`);
@@ -289,6 +346,7 @@ module.exports = {
unzip_meta: true,
async unzip({ folder }) {
assertSafeArchiveName(folder, 'folder');
const newFolder = await this.getNewArchiveFolder({ database: folder.slice(0, -4) });
await unzipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));
socket.emitChanged(`archive-folders-changed`);
@@ -298,6 +356,7 @@ module.exports = {
getZippedPath_meta: true,
async getZippedPath({ folder }) {
assertSafeArchiveName(folder, 'folder');
if (folder.endsWith('.zip')) {
return { filePath: path.join(archivedir(), folder) };
}

View File

@@ -55,6 +55,8 @@ function authMiddleware(req, res, next) {
'/stream',
'/storage/get-connections-for-login-page',
'/storage/set-admin-password',
'/storage/request-password-reset',
'/storage/reset-password',
'/auth/get-providers',
'/connections/dblogin-web',
'/connections/dblogin-app',

View File

@@ -9,6 +9,8 @@ const {
putCloudContent,
removeCloudCachedConnection,
getPromoWidgetData,
getPromoWidgetList,
getPromoWidgetPreview,
} = require('../utility/cloudIntf');
const connections = require('./connections');
const socket = require('../utility/socket');
@@ -33,8 +35,8 @@ module.exports = {
},
refreshPublicFiles_meta: true,
async refreshPublicFiles({ isRefresh }) {
await refreshPublicFiles(isRefresh);
async refreshPublicFiles({ isRefresh }, req) {
await refreshPublicFiles(isRefresh, req?.headers?.['x-ui-language']);
return {
status: 'ok',
};
@@ -286,7 +288,7 @@ module.exports = {
premiumPromoWidget_meta: true,
async premiumPromoWidget() {
const data = getPromoWidgetData();
const data = await getPromoWidgetData();
if (data?.state != 'data') {
return null;
}
@@ -296,6 +298,16 @@ module.exports = {
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',

View File

@@ -71,6 +71,7 @@ module.exports = {
const isLicenseValid = checkedLicense?.status == 'ok';
const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
const settingsConfig = storageConnectionError ? null : await storage.readConfig({ group: 'settings' });
storage.startRefreshLicense();
@@ -121,6 +122,7 @@ module.exports = {
allowPrivateCloud: platformInfo.isElectron || !!process.env.ALLOW_DBGATE_PRIVATE_CLOUD,
...currentVersion,
redirectToDbGateCloudLogin: !!process.env.REDIRECT_TO_DBGATE_CLOUD_LOGIN,
preferrendLanguage: settingsConfig?.['storage.language'] || process.env.LANGUAGE || null,
};
return configResult;
@@ -287,16 +289,11 @@ module.exports = {
const res = await lock.acquire('settings', async () => {
const currentValue = await this.loadSettings();
try {
let updated = currentValue;
let updated = {
...currentValue,
...values,
};
if (process.env.STORAGE_DATABASE) {
updated = {
...currentValue,
..._.mapValues(values, v => {
if (v === true) return 'true';
if (v === false) return 'false';
return v;
}),
};
await storage.writeConfig({
group: 'settings',
config: updated,

View File

@@ -14,15 +14,22 @@ 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, loadPermissionsFromRequest } = 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');
const { startTokenChecking } = require('../utility/authProxy');
const { extractConnectionsFromEnv } = require('../utility/envtools');
const { MissingCredentialsError } = require('../utility/exceptions');
const logger = getLogger('connections');
let volatileConnections = {};
let pendingTestSubprocesses = {}; // Map of conid -> subprocess for MS Entra auth flows
function getNamedArgs() {
const res = {};
@@ -57,55 +64,7 @@ function getDatabaseFileLabel(databaseFile) {
function getPortalCollections() {
if (process.env.CONNECTIONS) {
const connections = _.compact(process.env.CONNECTIONS.split(',')).map(id => ({
_id: id,
engine: process.env[`ENGINE_${id}`],
server: process.env[`SERVER_${id}`],
user: process.env[`USER_${id}`],
password: process.env[`PASSWORD_${id}`],
passwordMode: process.env[`PASSWORD_MODE_${id}`],
port: process.env[`PORT_${id}`],
databaseUrl: process.env[`URL_${id}`],
useDatabaseUrl: !!process.env[`URL_${id}`],
databaseFile: process.env[`FILE_${id}`]?.replace(
'%%E2E_TEST_DATA_DIRECTORY%%',
path.join(path.dirname(path.dirname(__dirname)), 'e2e-tests', 'tmpdata')
),
socketPath: process.env[`SOCKET_PATH_${id}`],
serviceName: process.env[`SERVICE_NAME_${id}`],
authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
defaultDatabase:
process.env[`DATABASE_${id}`] ||
(process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
singleDatabase: !!process.env[`DATABASE_${id}`] || !!process.env[`FILE_${id}`],
displayName: process.env[`LABEL_${id}`],
isReadOnly: process.env[`READONLY_${id}`],
databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
allowedDatabases: process.env[`ALLOWED_DATABASES_${id}`]?.replace(/\|/g, '\n'),
allowedDatabasesRegex: process.env[`ALLOWED_DATABASES_REGEX_${id}`],
parent: process.env[`PARENT_${id}`] || undefined,
useSeparateSchemas: !!process.env[`USE_SEPARATE_SCHEMAS_${id}`],
localDataCenter: process.env[`LOCAL_DATA_CENTER_${id}`],
// SSH tunnel
useSshTunnel: process.env[`USE_SSH_${id}`],
sshHost: process.env[`SSH_HOST_${id}`],
sshPort: process.env[`SSH_PORT_${id}`],
sshMode: process.env[`SSH_MODE_${id}`],
sshLogin: process.env[`SSH_LOGIN_${id}`],
sshPassword: process.env[`SSH_PASSWORD_${id}`],
sshKeyfile: process.env[`SSH_KEY_FILE_${id}`],
sshKeyfilePassword: process.env[`SSH_KEY_FILE_PASSWORD_${id}`],
// SSL
useSsl: process.env[`USE_SSL_${id}`],
sslCaFile: process.env[`SSL_CA_FILE_${id}`],
sslCertFile: process.env[`SSL_CERT_FILE_${id}`],
sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
trustServerCertificate: process.env[`SSL_TRUST_CERTIFICATE_${id}`],
}));
const connections = extractConnectionsFromEnv(process.env);
for (const conn of connections) {
for (const prop in process.env) {
@@ -116,7 +75,10 @@ function getPortalCollections() {
}
}
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'DBGM-00005 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(
@@ -222,6 +184,15 @@ module.exports = {
);
}
await this.checkUnsavedConnectionsLimit();
if (process.env.STORAGE_DATABASE && process.env.CONNECTIONS) {
const storage = require('./storage');
try {
await storage.fillStorageConnectionsFromEnv();
} catch (err) {
logger.error(extractErrorLogData(err), 'DBGM-00268 Error filling storage connections from env');
}
}
},
list_meta: true,
@@ -231,10 +202,10 @@ module.exports = {
const storageConnections = await storage.connections(req);
if (storageConnections) {
return storageConnections;
return storageConnections.map(maskConnection);
}
if (portalConnections) {
if (platformInfo.allowShellConnection) return portalConnections;
if (platformInfo.allowShellConnection) return portalConnections.map(x => encryptConnection(x));
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, loadedPermissions));
}
return (await this.datastore.find()).filter(x => connectionHasPermission(x, loadedPermissions));
@@ -270,14 +241,60 @@ module.exports = {
);
pipeForkLogs(subprocess);
subprocess.send({ ...connection, requestDbList });
return new Promise(resolve => {
return new Promise((resolve, reject) => {
let isWaitingForVolatile = false;
const cleanup = () => {
if (connection._id && pendingTestSubprocesses[connection._id]) {
delete pendingTestSubprocesses[connection._id];
}
};
subprocess.on('message', resp => {
if (handleProcessCommunication(resp, subprocess)) return;
// @ts-ignore
const { msgtype } = resp;
const { msgtype, missingCredentialsDetail } = resp;
if (msgtype == 'connected' || msgtype == 'error') {
cleanup();
resolve(resp);
}
if (msgtype == 'missingCredentials') {
if (missingCredentialsDetail?.redirectToDbLogin) {
// Store the subprocess for later when volatile connection is ready
isWaitingForVolatile = true;
pendingTestSubprocesses[connection._id] = {
subprocess,
requestDbList,
};
// Return immediately with redirectToDbLogin status in the old format
resolve({
missingCredentials: true,
detail: {
...missingCredentialsDetail,
keepErrorResponseFromApi: true,
},
});
return;
}
reject(new MissingCredentialsError(missingCredentialsDetail));
}
});
subprocess.on('exit', code => {
// If exit happens while waiting for volatile, that's expected
if (isWaitingForVolatile && code === 0) {
cleanup();
return;
}
cleanup();
if (code !== 0) {
reject(new Error(`Test subprocess exited with code ${code}`));
}
});
subprocess.on('error', err => {
cleanup();
reject(err);
});
});
},
@@ -310,6 +327,38 @@ module.exports = {
return testRes;
} else {
volatileConnections[res._id] = res;
// Check if there's a pending test subprocess waiting for this volatile connection
const pendingTest = pendingTestSubprocesses[conid];
if (pendingTest) {
const { subprocess, requestDbList } = pendingTest;
try {
// Send the volatile connection to the waiting subprocess
subprocess.send({ ...res, requestDbList, isVolatileResolved: true });
// Wait for the test result and emit it as an event
subprocess.once('message', resp => {
if (handleProcessCommunication(resp, subprocess)) return;
const { msgtype } = resp;
if (msgtype == 'connected' || msgtype == 'error') {
// Emit SSE event with test result
socket.emit(`connection-test-result-${conid}`, {
...resp,
volatileConId: res._id,
});
delete pendingTestSubprocesses[conid];
}
});
} catch (err) {
logger.error(extractErrorLogData(err), 'DBGM-00118 Error sending volatile connection to test subprocess');
socket.emit(`connection-test-result-${conid}`, {
msgtype: 'error',
error: err.message,
});
delete pendingTestSubprocesses[conid];
}
}
return res;
}
},
@@ -435,15 +484,69 @@ module.exports = {
const storageConnection = await storage.getConnection({ conid });
if (storageConnection) {
return storageConnection;
return mask ? maskConnection(storageConnection) : storageConnection;
}
if (portalConnections) {
const res = portalConnections.find(x => x._id == conid) || null;
return mask && !platformInfo.allowShellConnection ? maskConnection(res) : res;
return mask && !platformInfo.allowShellConnection ? maskConnection(res) : encryptConnection(res);
}
const res = await this.datastore.get(conid);
return res || null;
if (res) return res;
// In a forked runner-script child process, ask the parent for connections that may be
// volatile (in-memory only, e.g. ask-for-password). We only do this when
// there really is a parent (process.send exists) to avoid an infinite loop
// when the parent's own getCore falls through here.
// The check is intentionally narrow: only runner scripts pass
// --process-display-name script, so connect/session/ssh-forward subprocesses
// are not affected and continue to return null immediately.
if (process.send && processArgs.processDisplayName === 'script') {
const conn = await new Promise(resolve => {
let resolved = false;
const cleanup = () => {
process.removeListener('message', handler);
process.removeListener('disconnect', onDisconnect);
clearTimeout(timeout);
};
const settle = value => {
if (!resolved) {
resolved = true;
cleanup();
resolve(value);
}
};
const handler = message => {
if (message?.msgtype === 'volatile-connection-response' && message.conid === conid) {
settle(message.conn || null);
}
};
const onDisconnect = () => settle(null);
const timeout = setTimeout(() => settle(null), 5000);
// Don't let the timer alone keep the process alive if all other work is done
timeout.unref();
process.on('message', handler);
process.once('disconnect', onDisconnect);
try {
process.send({ msgtype: 'get-volatile-connection', conid });
} catch {
settle(null);
}
});
if (conn) {
volatileConnections[conn._id] = conn; // cache for subsequent calls
return conn;
}
}
return null;
},
get_meta: true,
@@ -453,6 +556,9 @@ module.exports = {
_id: '__model',
};
}
if (!conid) {
return null;
}
await testConnectionPermission(conid, req);
return this.getCore({ conid, mask: true });
},
@@ -502,7 +608,11 @@ module.exports = {
state,
client: 'web',
});
res.redirect(authResp.url);
if (authResp?.url) {
res.redirect(authResp.url);
return;
}
res.json({ error: 'No URL returned from auth provider' });
},
dbloginApp_meta: true,

View File

@@ -15,6 +15,7 @@ const {
getLogger,
extractErrorLogData,
filterStructureBySchema,
serializeJsTypesForJsonStringify,
} = require('dbgate-tools');
const { html, parse } = require('diff2html');
const { handleProcessCommunication } = require('../utility/processComm');
@@ -29,7 +30,17 @@ const generateDeploySql = require('../shell/generateDeploySql');
const { createTwoFilesPatch } = require('diff');
const diff2htmlPage = require('../utility/diff2htmlPage');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission, hasPermission, loadPermissionsFromRequest, loadTablePermissionsFromRequest, getTablePermissionRole, loadDatabasePermissionsFromRequest, getDatabasePermissionRole, getTablePermissionRoleLevelIndex, testDatabaseRolePermission } = 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');
@@ -84,10 +95,12 @@ module.exports = {
}
},
handle_response(conid, database, { msgid, ...response }) {
const [resolve, reject, additionalData] = this.requests[msgid];
resolve(response);
if (additionalData?.auditLogger) {
additionalData?.auditLogger(response);
const [resolve, reject, additionalData] = this.requests[msgid] || [];
if (resolve) {
resolve(response);
if (additionalData?.auditLogger) {
additionalData?.auditLogger(response);
}
}
delete this.requests[msgid];
},
@@ -100,7 +113,7 @@ module.exports = {
socket.emitChanged(`database-status-changed`, { conid, database });
},
handle_ping() { },
handle_ping() {},
// session event handlers
@@ -155,6 +168,11 @@ module.exports = {
if (!connection) {
throw new Error(`databaseConnections: Connection with conid="${conid}" not found`);
}
if (connection.engine?.endsWith('@rest')) {
return { isApiConnection: true };
}
if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
}
@@ -209,12 +227,13 @@ module.exports = {
this.close(conid, database, false);
});
subprocess.send({
const connectMessage = serializeJsTypesForJsonStringify({
msgtype: 'connect',
connection: { ...connection, database },
structure: lastClosed ? lastClosed.structure : null,
globalSettings: await config.getSettings(),
});
subprocess.send(connectMessage);
return newOpened;
},
@@ -222,9 +241,10 @@ module.exports = {
sendRequest(conn, message, additionalData = {}) {
const msgid = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
this.requests[msgid] = [resolve, reject, additionalData];
this.requests[msgid] = [resolve, reject, additionalData, conn.conid, conn.database];
try {
conn.subprocess.send({ msgid, ...message });
const serializedMessage = serializeJsTypesForJsonStringify({ msgid, ...message });
conn.subprocess.send(serializedMessage);
} catch (err) {
logger.error(extractErrorLogData(err), 'DBGM-00115 Error sending request do process');
this.close(conn.conid, conn.database);
@@ -246,33 +266,34 @@ module.exports = {
},
sqlSelect_meta: true,
async sqlSelect({ conid, database, select, auditLogSessionGroup }, req) {
async sqlSelect({ conid, database, select, commandTimeout, auditLogSessionGroup }, req) {
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(
opened,
{ msgtype: 'sqlSelect', select },
{ msgtype: 'sqlSelect', select, commandTimeout },
{
auditLogger:
auditLogSessionGroup && select?.from?.name?.pureName
? response => {
sendToAuditLog(req, {
category: 'dbop',
component: 'DatabaseConnectionsController',
event: 'sql.select',
action: 'select',
severity: 'info',
conid,
database,
schemaName: select?.from?.name?.schemaName,
pureName: select?.from?.name?.pureName,
sumint1: response?.rows?.length,
sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${select?.from?.name?.pureName
sendToAuditLog(req, {
category: 'dbop',
component: 'DatabaseConnectionsController',
event: 'sql.select',
action: 'select',
severity: 'info',
conid,
database,
schemaName: select?.from?.name?.schemaName,
pureName: select?.from?.name?.pureName,
sumint1: response?.rows?.length,
sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${
select?.from?.name?.pureName
}`,
sessionGroup: auditLogSessionGroup,
message: `Loaded table data from ${select?.from?.name?.pureName}`,
});
}
sessionGroup: auditLogSessionGroup,
message: `Loaded table data from ${select?.from?.name?.pureName}`,
});
}
: null,
}
);
@@ -325,9 +346,12 @@ module.exports = {
},
collectionData_meta: true,
async collectionData({ conid, database, options, auditLogSessionGroup }, req) {
async collectionData({ conid, database, options, commandTimeout, auditLogSessionGroup }, req) {
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
if (commandTimeout && options) {
options.commandTimeout = commandTimeout;
}
const res = await this.sendRequest(
opened,
{ msgtype: 'collectionData', options },
@@ -335,21 +359,21 @@ module.exports = {
auditLogger:
auditLogSessionGroup && options?.pureName
? response => {
sendToAuditLog(req, {
category: 'dbop',
component: 'DatabaseConnectionsController',
event: 'nosql.collectionData',
action: 'select',
severity: 'info',
conid,
database,
pureName: options?.pureName,
sumint1: response?.result?.rows?.length,
sessionParam: `${conid}::${database}::${options?.pureName}`,
sessionGroup: auditLogSessionGroup,
message: `Loaded collection data ${options?.pureName}`,
});
}
sendToAuditLog(req, {
category: 'dbop',
component: 'DatabaseConnectionsController',
event: 'nosql.collectionData',
action: 'select',
severity: 'info',
conid,
database,
pureName: options?.pureName,
sumint1: response?.result?.rows?.length,
sessionParam: `${conid}::${database}::${options?.pureName}`,
sessionGroup: auditLogSessionGroup,
message: `Loaded collection data ${options?.pureName}`,
});
}
: null,
}
);
@@ -382,6 +406,12 @@ module.exports = {
return null;
},
dispatchRedisKeysChanged_meta: true,
dispatchRedisKeysChanged({ conid, database }) {
socket.emit(`redis-keys-changed-${conid}-${database}`);
return null;
},
loadKeys_meta: true,
async loadKeys({ conid, database, root, filter, limit }, req) {
await testConnectionPermission(conid, req);
@@ -451,14 +481,23 @@ module.exports = {
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const tablePermissions = await loadTablePermissionsFromRequest(req);
const databasePermissionRole = getDatabasePermissionRole(conid, database, databasePermissions);
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);
const role = getTablePermissionRole(
conid,
database,
'tables',
operation.schemaName,
operation.pureName,
tablePermissions,
databasePermissionRole
);
if (getTablePermissionRoleLevelIndex(role) < getTablePermissionRoleLevelIndex(requiredRole)) {
throw new Error('DBGM-00262 Permission not granted');
}
@@ -475,6 +514,20 @@ module.exports = {
return res.result || null;
},
multiCallMethod_meta: true,
async multiCallMethod({ conid, database, callList }, req) {
await testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'multiCallMethod', callList });
if (res.errorMessage) {
return {
errorMessage: res.errorMessage,
};
}
return res.result || null;
},
status_meta: true,
async status({ conid, database }, req) {
if (!conid) {
@@ -532,6 +585,24 @@ module.exports = {
};
},
pingDatabases_meta: true,
async pingDatabases({ databases }, req) {
if (!databases || !Array.isArray(databases)) return { status: 'ok' };
for (const { conid, database } of databases) {
if (!conid || !database) continue;
const 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), 'DBGM-00308 Error pinging DB connection');
this.close(conid, database);
}
}
}
return { status: 'ok' };
},
refresh_meta: true,
async refresh({ conid, database, keepOpen }, req) {
await testConnectionPermission(conid, req);
@@ -574,6 +645,15 @@ module.exports = {
structure: existing.structure,
};
socket.emitChanged(`database-status-changed`, { conid, database });
// Reject all pending requests for this connection
for (const [msgid, entry] of Object.entries(this.requests)) {
const [resolve, reject, additionalData, reqConid, reqDatabase] = entry;
if (reqConid === conid && reqDatabase === database) {
reject('DBGM-00309 Database connection closed');
delete this.requests[msgid];
}
}
}
},
@@ -628,7 +708,15 @@ module.exports = {
function applyTablePermissionRole(list, objectTypeField) {
const res = [];
for (const item of list ?? []) {
const tablePermissionRole = getTablePermissionRole(conid, database, objectTypeField, item.schemaName, item.pureName, tablePermissions, databasePermissionRole);
const tablePermissionRole = getTablePermissionRole(
conid,
database,
objectTypeField,
item.schemaName,
item.pureName,
tablePermissions,
databasePermissionRole
);
if (tablePermissionRole != 'deny') {
res.push({
...item,
@@ -647,7 +735,7 @@ module.exports = {
functions: applyTablePermissionRole(opened.structure.functions, 'functions'),
triggers: applyTablePermissionRole(opened.structure.triggers, 'triggers'),
collections: applyTablePermissionRole(opened.structure.collections, 'collections'),
}
};
return res;
}
@@ -881,17 +969,17 @@ module.exports = {
return {
...(command == 'backup'
? driver.backupDatabaseCommand(
connection,
{ outputFile, database, options, selectedTables, skippedTables, argsFormat },
// @ts-ignore
externalTools
)
connection,
{ outputFile, database, options, selectedTables, skippedTables, argsFormat },
// @ts-ignore
externalTools
)
: driver.restoreDatabaseCommand(
connection,
{ inputFile, database, options, argsFormat },
// @ts-ignore
externalTools
)),
connection,
{ inputFile, database, options, argsFormat },
// @ts-ignore
externalTools
)),
transformMessage: driver.transformNativeCommandMessage
? message => driver.transformNativeCommandMessage(message, command)
: null,
@@ -990,7 +1078,10 @@ module.exports = {
async executeSessionQuery({ sesid, conid, database, sql }, req) {
await testConnectionPermission(conid, req);
logger.info({ sesid, sql }, 'DBGM-00010 Processing query');
sessions.dispatchMessage(sesid, 'Query execution started');
sessions.dispatchMessage(sesid, {
message: 'Query execution started',
sql,
});
const opened = await this.ensureOpened(conid, database);
opened.subprocess.send({ msgtype: 'executeSessionQuery', sql, sesid });

View File

@@ -15,7 +15,8 @@ const getDiagramExport = require('../utility/getDiagramExport');
const apps = require('./apps');
const getMapExport = require('../utility/getMapExport');
const dbgateApi = require('../shell');
const { getLogger } = require('dbgate-tools');
const { getLogger, getSqlFrontMatter } = require('dbgate-tools');
const yaml = require('js-yaml');
const platformInfo = require('../utility/platformInfo');
const { checkSecureFilePathsWithoutDirectory, checkSecureDirectories } = require('../utility/security');
const { copyAppLogsIntoFile, getRecentAppLogRecords } = require('../utility/appLogStore');
@@ -35,13 +36,46 @@ function deserialize(format, text) {
module.exports = {
list_meta: true,
async list({ folder }, req) {
async list({ folder, parseFrontMatter }, req) {
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 }));
return files;
const fileNames = await fs.readdir(dir);
if (!parseFrontMatter) {
return fileNames.map(file => ({ folder, file }));
}
const result = [];
for (const file of fileNames) {
const item = { folder, file };
let fh;
try {
fh = await require('fs').promises.open(path.join(dir, file), 'r');
const buf = new Uint8Array(512);
const { bytesRead } = await fh.read(buf, 0, 512, 0);
let text = Buffer.from(buf.buffer, 0, bytesRead).toString('utf-8');
if (text.includes('-- >>>') && !text.includes('-- <<<')) {
const stat = await fh.stat();
const fullSize = Math.min(stat.size, 4096);
if (fullSize > 512) {
const fullBuf = new Uint8Array(fullSize);
const { bytesRead: fullBytesRead } = await fh.read(fullBuf, 0, fullSize, 0);
text = Buffer.from(fullBuf.buffer, 0, fullBytesRead).toString('utf-8');
}
}
const fm = getSqlFrontMatter(text, yaml);
if (fm?.connectionId) item.connectionId = fm.connectionId;
if (fm?.databaseName) item.databaseName = fm.databaseName;
} catch (e) {
// ignore read errors for individual files
} finally {
if (fh) await fh.close().catch(() => {});
}
result.push(item);
}
return result;
},
listAll_meta: true,
@@ -68,6 +102,7 @@ module.exports = {
await fs.unlink(path.join(filesdir(), folder, file));
socket.emitChanged(`files-changed`, { folder });
socket.emitChanged(`all-files-changed`);
this.emitChangedFolder(folder);
return true;
},
@@ -140,6 +175,15 @@ module.exports = {
return deserialize(format, text);
},
emitChangedFolder(folder) {
if (folder == 'themes') {
socket.emitChanged(`file-themes-changed`);
}
if (folder == 'favorites') {
socket.emitChanged('files-changed-favorites');
}
},
save_meta: true,
async save({ folder, file, data, format }, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
@@ -173,6 +217,8 @@ module.exports = {
if (folder == 'shell') {
scheduler.reload();
}
this.emitChangedFolder(folder);
return true;
}
},
@@ -240,8 +286,15 @@ module.exports = {
},
exportDiagram_meta: true,
async exportDiagram({ filePath, html, css, themeType, themeClassName, watermark }) {
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeClassName, watermark));
async exportDiagram({ filePath, html, css, themeType, themeVariables, watermark }) {
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeVariables, watermark));
return true;
},
exportDiagramPng_meta: true,
async exportDiagramPng({ filePath, pngBase64 }) {
const base64 = pngBase64.replace(/^data:image\/png;base64,/, '');
await fs.writeFile(filePath, Buffer.from(base64, 'base64'));
return true;
},
@@ -346,4 +399,20 @@ module.exports = {
}
return res;
},
getFileThemes_meta: true,
async getFileThemes(_params, req) {
const loadedPermissions = await loadPermissionsFromRequest(req);
if (!hasPermission(`files/themes/read`, loadedPermissions)) return [];
const dir = path.join(filesdir(), 'themes');
if (!(await fs.exists(dir))) return [];
const files = await fs.readdir(dir);
const res = [];
for (const file of files) {
const filePath = path.join(dir, file);
const text = await fs.readFile(filePath, { encoding: 'utf-8' });
res.push(JSON.parse(text));
}
return res;
},
};

View File

@@ -1,5 +1,8 @@
const { filterName } = require('dbgate-tools');
const { filterName, getLogger, extractErrorLogData } = require('dbgate-tools');
const logger = getLogger('jsldata');
const { jsldir, archivedir } = require('../utility/directories');
const fs = require('fs');
const path = require('path');
const lineReader = require('line-reader');
const _ = require('lodash');
const { __ } = require('lodash/fp');
@@ -149,6 +152,10 @@ module.exports = {
getRows_meta: true,
async getRows({ jslid, offset, limit, filters, sort, formatterFunction }) {
const fileName = getJslFileName(jslid);
if (!fs.existsSync(fileName)) {
return [];
}
const datastore = await this.ensureDatastore(jslid, formatterFunction);
return datastore.getRows(offset, limit, _.isEmpty(filters) ? null : filters, _.isEmpty(sort) ? null : sort);
},
@@ -159,6 +166,72 @@ module.exports = {
return fs.existsSync(fileName);
},
streamRows_meta: {
method: 'get',
raw: true,
},
streamRows(req, res) {
const { jslid } = req.query;
if (!jslid) {
res.status(400).json({ apiErrorMessage: 'Missing jslid' });
return;
}
// Reject file:// jslids — they resolve to arbitrary server-side paths
if (jslid.startsWith('file://')) {
res.status(403).json({ apiErrorMessage: 'Forbidden jslid scheme' });
return;
}
const fileName = getJslFileName(jslid);
if (!fs.existsSync(fileName)) {
res.status(404).json({ apiErrorMessage: 'File not found' });
return;
}
// Dereference symlinks and normalize case (Windows) before the allow-list check.
// realpathSync is safe here because existsSync confirmed the file is present.
// path.resolve() alone cannot dereference symlinks, so a symlink inside an allowed
// root could otherwise point to an arbitrary external path.
const normalize = p => (process.platform === 'win32' ? p.toLowerCase() : p);
const resolveRoot = r => { try { return fs.realpathSync(r); } catch { return path.resolve(r); } };
let realFile;
try {
realFile = fs.realpathSync(fileName);
} catch {
res.status(403).json({ apiErrorMessage: 'Forbidden path' });
return;
}
const allowedRoots = [jsldir(), archivedir()].map(r => normalize(resolveRoot(r)) + path.sep);
const isAllowed = allowedRoots.some(root => normalize(realFile).startsWith(root));
if (!isAllowed) {
logger.warn({ jslid, realFile }, 'DBGM-00000 streamRows rejected path outside allowed roots');
res.status(403).json({ apiErrorMessage: 'Forbidden path' });
return;
}
res.setHeader('Content-Type', 'application/x-ndjson');
res.setHeader('Cache-Control', 'no-cache');
const stream = fs.createReadStream(realFile, 'utf-8');
req.on('close', () => {
stream.destroy();
});
stream.on('error', err => {
logger.error(extractErrorLogData(err), 'DBGM-00000 Error streaming JSONL file');
if (!res.headersSent) {
res.status(500).json({ apiErrorMessage: 'Stream error' });
} else {
res.end();
}
});
stream.pipe(res);
},
getStats_meta: true,
getStats({ jslid }) {
const file = `${getJslFileName(jslid)}.stats`;

View File

@@ -33,19 +33,35 @@ function readCore(reader, skip, limit, filter) {
});
}
module.exports = {
read_meta: true,
async read({ skip, limit, filter }) {
function readJsonl({ skip, limit, filter }) {
return new Promise(async (resolve, reject) => {
const fileName = path.join(datadir(), 'query-history.jsonl');
// @ts-ignore
if (!(await fs.exists(fileName))) return [];
if (!(await fs.exists(fileName))) return resolve([]);
const reader = fsReverse(fileName);
const res = await readCore(reader, skip, limit, filter);
return res;
resolve(res);
});
}
module.exports = {
read_meta: true,
async read({ skip, limit, filter }, req) {
const storage = require('./storage');
const storageResult = await storage.readQueryHistory({ skip, limit, filter }, req);
if (storageResult) return storageResult;
return readJsonl({ skip, limit, filter });
},
write_meta: true,
async write({ data }) {
async write({ data }, req) {
const storage = require('./storage');
const written = await storage.writeQueryHistory({ data }, req);
if (written) {
socket.emit('query-history-changed');
return 'OK';
}
const fileName = path.join(datadir(), 'query-history.jsonl');
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
socket.emit('query-history-changed');

View File

@@ -0,0 +1,41 @@
module.exports = {
disconnect_meta: true,
async disconnect({ conid }, req) {
return null;
},
getApiInfo_meta: true,
async getApiInfo({ conid }, req) {
return null;
},
restStatus_meta: true,
async restStatus() {
return {};
},
ping_meta: true,
async ping({ conidArray, strmid }) {
return null;
},
refresh_meta: true,
async refresh({ conid, keepOpen }, req) {
return null;
},
testConnection_meta: true,
async testConnection({ conid }, req) {
return null;
},
execute_meta: true,
async execute({ conid, method, endpoint, parameters, server }, req) {
return null;
},
apiQuery_meta: true,
async apiQuery({ conid, server, query, variables }, req) {
return null;
},
};

View File

@@ -10,6 +10,7 @@ const {
extractShellApiPlugins,
compileShellApiFunctionName,
jsonScriptToJavascript,
assertValidShellApiFunctionName,
getLogger,
safeJsonParse,
pinoLogRecordToMessageRecord,
@@ -54,19 +55,23 @@ logger.info('DBGM-00014 Finished job script');
dbgateApi.runScript(run);
`;
const loaderScriptTemplate = (prefix, functionName, props, runid) => `
const loaderScriptTemplate = (functionName, props, runid) => {
const plugins = extractShellApiPlugins(functionName, props);
const prefix = plugins.map(packageName => `// @require ${packageName}\n`).join('');
return `
${prefix}
const dbgateApi = require(process.env.DBGATE_API);
dbgateApi.initializeApiEnvironment();
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
${requirePluginsTemplate(plugins)}
require=null;
async function run() {
const reader=await ${compileShellApiFunctionName(functionName)}(${JSON.stringify(props)});
const writer=await dbgateApi.collectorWriter({runid: '${runid}'});
const writer=await dbgateApi.collectorWriter({runid: ${JSON.stringify(runid)}});
await dbgateApi.copyStream(reader, writer);
}
dbgateApi.runScript(run);
`;
};
module.exports = {
/** @type {import('dbgate-types').OpenedRunner[]} */
@@ -172,7 +177,7 @@ module.exports = {
byline(subprocess.stderr).on('data', pipeDispatcher('error'));
subprocess.on('exit', code => {
// console.log('... EXITED', code);
this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' });
this.rejectRequest(runid, { message: 'DBGM-00281 No data returned, maybe input data source is too big' });
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);
@@ -196,6 +201,27 @@ module.exports = {
// @ts-ignore
const { msgtype } = message;
if (handleProcessCommunication(message, subprocess)) return;
if (msgtype === 'get-volatile-connection') {
const connections = require('./connections');
// @ts-ignore
const conid = message.conid;
if (!conid || typeof conid !== 'string') return;
const trySend = payload => {
if (!subprocess.connected) return;
try {
subprocess.send(payload);
} catch {
// child disconnected between the check and the send — ignore
}
};
connections.getCore({ conid }).then(conn => {
trySend({ msgtype: 'volatile-connection-response', conid, conn: conn?.unsaved ? conn : null });
}).catch(err => {
logger.error({ ...extractErrorLogData(err), conid }, 'DBGM-00000 Error resolving volatile connection for child process');
trySend({ msgtype: 'volatile-connection-response', conid, conn: null });
});
return;
}
this[`handle_${msgtype}`](runid, message);
});
return _.pick(newOpened, ['runid']);
@@ -225,7 +251,7 @@ module.exports = {
subprocess.on('exit', code => {
console.log('... EXITED', code);
logger.info({ code, pid: subprocess.pid }, 'DBGM-00017 Exited process');
this.dispatchMessage(runid, `Finished external process with code ${code}`);
this.dispatchMessage(runid, `DBGM-00282 Finished external process with code ${code}`);
socket.emit(`runner-done-${runid}`, code);
if (onFinished) {
onFinished();
@@ -233,7 +259,7 @@ module.exports = {
this.opened = this.opened.filter(x => x.runid != runid);
});
subprocess.on('spawn', () => {
this.dispatchMessage(runid, `Started external process ${command}`);
this.dispatchMessage(runid, `DBGM-00283 Started external process ${command}`);
});
subprocess.on('error', error => {
console.log('... ERROR subprocess', error);
@@ -274,14 +300,12 @@ module.exports = {
start_meta: true,
async start({ script }, req) {
await testStandardPermission('run-shell-script', req);
const runid = crypto.randomUUID();
if (script.type == 'json') {
if (!platformInfo.isElectron) {
if (!checkSecureDirectoriesInScript(script)) {
return { errorMessage: 'Unallowed directories in script' };
return { errorMessage: 'DBGM-00284 Unallowed directories in script' };
}
}
@@ -291,6 +315,8 @@ module.exports = {
return this.startCore(runid, scriptTemplate(js, false));
}
await testStandardPermission('run-shell-script', req);
if (!platformInfo.allowShellScripting) {
sendToAuditLog(req, {
category: 'shell',
@@ -299,10 +325,10 @@ module.exports = {
action: 'script',
severity: 'warn',
detail: script,
message: 'Scripts are not allowed',
message: 'DBGM-00285 Scripts are not allowed',
});
return { errorMessage: 'Shell scripting is not allowed' };
return { errorMessage: 'DBGM-00286 Shell scripting is not allowed' };
}
sendToAuditLog(req, {
@@ -312,7 +338,7 @@ module.exports = {
action: 'script',
severity: 'info',
detail: script,
message: 'Running JS script',
message: 'DBGM-00287 Running JS script',
});
return this.startCore(runid, scriptTemplate(script, false));
@@ -327,7 +353,7 @@ module.exports = {
async cancel({ runid }) {
const runner = this.opened.find(x => x.runid == runid);
if (!runner) {
throw new Error('Invalid runner');
throw new Error('DBGM-00288 Invalid runner');
}
runner.subprocess.kill();
return { state: 'ok' };
@@ -353,17 +379,15 @@ module.exports = {
async loadReader({ functionName, props }) {
if (!platformInfo.isElectron) {
if (props?.fileName && !checkSecureDirectories(props.fileName)) {
return { errorMessage: 'Unallowed file' };
return { errorMessage: 'DBGM-00289 Unallowed file' };
}
}
const prefix = extractShellApiPlugins(functionName)
.map(packageName => `// @require ${packageName}\n`)
.join('');
const promise = new Promise((resolve, reject) => {
assertValidShellApiFunctionName(functionName);
const runid = crypto.randomUUID();
this.requests[runid] = { resolve, reject, exitOnStreamError: true };
this.startCore(runid, loaderScriptTemplate(prefix, functionName, props, runid));
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
});
return promise;
},
@@ -371,7 +395,7 @@ module.exports = {
scriptResult_meta: true,
async scriptResult({ script }) {
if (script.type != 'json') {
return { errorMessage: 'Only JSON scripts are allowed' };
return { errorMessage: 'DBGM-00290 Only JSON scripts are allowed' };
}
const promise = new Promise(async (resolve, reject) => {

View File

@@ -171,7 +171,7 @@ module.exports = {
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
const res = [];
for (const db of opened?.databases ?? []) {
const databasePermissionRole = getDatabasePermissionRole(db.id, db.name, databasePermissions);
const databasePermissionRole = getDatabasePermissionRole(conid, db.name, databasePermissions);
if (databasePermissionRole != 'deny') {
res.push({
...db,
@@ -209,11 +209,11 @@ 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), 'DBGM-00121 Error pinging server connection');

View File

@@ -83,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);
},
@@ -97,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,
@@ -182,7 +198,10 @@ module.exports = {
});
logger.info({ sesid, sql }, 'DBGM-00019 Processing query');
this.dispatchMessage(sesid, 'Query execution started');
this.dispatchMessage(sesid, {
message: 'Query execution started',
sql,
});
session.subprocess.send({
msgtype: 'executeQuery',
sql,
@@ -209,6 +228,19 @@ module.exports = {
return { state: 'ok' };
},
setIsolationLevel_meta: true,
async setIsolationLevel({ sesid, level }) {
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
logger.info({ sesid, level }, 'DBGM-00315 Setting transaction isolation level');
session.subprocess.send({ msgtype: 'setIsolationLevel', level });
return { state: 'ok' };
},
executeReader_meta: true,
async executeReader({ conid, database, sql, queryName, appFolder }) {
const { sesid } = await this.create({ conid, database });

View File

@@ -1,5 +1,5 @@
module.exports = {
version: '6.0.0-alpha.1',
version: '7.0.0-alpha.1',
buildTime: '2024-12-01T00:00:00Z'
};

View File

@@ -147,6 +147,7 @@ const shell = require('./shell/index');
global.DBGATE_PACKAGES = {
'dbgate-tools': require('dbgate-tools'),
'dbgate-sqltree': require('dbgate-sqltree'),
'dbgate-datalib': require('dbgate-datalib'),
};
if (processArgs.startProcess) {

View File

@@ -14,6 +14,7 @@ const socket = require('./utility/socket');
const connections = require('./controllers/connections');
const serverConnections = require('./controllers/serverConnections');
const databaseConnections = require('./controllers/databaseConnections');
const restConnections = require('./controllers/restConnections');
const metadata = require('./controllers/metadata');
const sessions = require('./controllers/sessions');
const runners = require('./controllers/runners');
@@ -267,6 +268,7 @@ function useAllControllers(app, electron) {
useController(app, electron, '/auth', auth);
useController(app, electron, '/cloud', cloud);
useController(app, electron, '/team-files', teamFiles);
useController(app, electron, '/rest-connections', restConnections);
}
function setElectronSender(electronSender) {

View File

@@ -1,6 +1,6 @@
const childProcessChecker = require('../utility/childProcessChecker');
const requireEngineDriver = require('../utility/requireEngineDriver');
const { connectUtility } = require('../utility/connectUtility');
const { connectUtility, getRestAuthFromConnection } = require('../utility/connectUtility');
const { handleProcessCommunication } = require('../utility/processComm');
const { pickSafeConnectionInfo } = require('../utility/crypting');
const _ = require('lodash');
@@ -18,13 +18,39 @@ Platform: ${process.platform}
function start() {
childProcessChecker();
process.on('message', async connection => {
let isWaitingForVolatile = false;
const handleConnection = async connection => {
// @ts-ignore
const { requestDbList } = connection;
if (handleProcessCommunication(connection)) return;
try {
const driver = requireEngineDriver(connection);
const dbhan = await connectUtility(driver, connection, 'app');
const connectionChanged = driver?.beforeConnectionSave ? driver.beforeConnectionSave(connection) : connection;
if (driver?.databaseEngineTypes?.includes('rest')) {
connectionChanged.restAuth = getRestAuthFromConnection(connection);
}
if (!connection.isVolatileResolved) {
if (connectionChanged.useRedirectDbLogin) {
process.send({
msgtype: 'missingCredentials',
missingCredentialsDetail: {
// @ts-ignore
conid: connection._id,
redirectToDbLogin: true,
keepErrorResponseFromApi: true,
},
});
// Don't exit - wait for volatile connection to be sent
isWaitingForVolatile = true;
return;
}
}
const dbhan = await connectUtility(driver, connectionChanged, 'app');
let version = {
version: 'Unknown',
};
@@ -45,6 +71,16 @@ function start() {
}
process.exit(0);
};
process.on('message', async connection => {
// If we're waiting for volatile and receive a new connection, use it
if (isWaitingForVolatile) {
isWaitingForVolatile = false;
await handleConnection(connection);
} else {
await handleConnection(connection);
}
});
}

View File

@@ -234,12 +234,12 @@ async function handleRunOperation({ msgid, operation, useTransaction }, skipRead
}
}
async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false) {
async function handleQueryData({ msgid, sql, range, commandTimeout }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
const res = await driver.query(dbhan, sql, { range });
const res = await driver.query(dbhan, sql, { range, commandTimeout });
process.send({ msgtype: 'response', msgid, ...serializeJsTypesForJsonStringify(res) });
} catch (err) {
process.send({
@@ -250,11 +250,11 @@ async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false)
}
}
async function handleSqlSelect({ msgid, select }) {
async function handleSqlSelect({ msgid, select, commandTimeout }) {
const driver = requireEngineDriver(storedConnection);
const dmp = driver.createDumper();
dumpSqlSelect(dmp, select);
return handleQueryData({ msgid, sql: dmp.s, range: select.range }, true);
return handleQueryData({ msgid, sql: dmp.s, range: select.range, commandTimeout }, true);
}
async function handleDriverDataCore(msgid, callMethod, { logName }) {
@@ -368,6 +368,107 @@ async function handleSaveTableData({ msgid, changeSet }) {
}
}
async function handleMultiCallMethod({ msgid, callList }) {
try {
const driver = requireEngineDriver(storedConnection);
await driver.invokeMethodCallList(dbhan, callList);
// for (const change of changeSet.changes) {
// if (change.type === 'string') {
// await driver.query(dbhan, `SET "${change.key}" "${change.value}"`);
// } else if (change.type === 'json') {
// await driver.query(dbhan, `JSON.SET "${change.key}" $ '${change.value.replace(/'/g, "\\'")}'`);
// } else if (change.type === 'hash') {
// if (change.updates && Array.isArray(change.updates)) {
// for (const update of change.updates) {
// await driver.query(dbhan, `HSET "${change.key}" "${update.key}" "${update.value}"`);
// if (update.ttl !== undefined && update.ttl !== null && update.ttl !== -1) {
// try {
// await dbhan.client.call('HEXPIRE', change.key, update.ttl, 'FIELDS', 1, update.key);
// } catch (e) {}
// }
// }
// }
// if (change.inserts && Array.isArray(change.inserts)) {
// for (const insert of change.inserts) {
// await driver.query(dbhan, `HSET "${change.key}" "${insert.key}" "${insert.value}"`);
// if (insert.ttl !== undefined && insert.ttl !== null && insert.ttl !== -1) {
// try {
// await dbhan.client.call('HEXPIRE', change.key, insert.ttl, 'FIELDS', 1, insert.key);
// } catch (e) {}
// }
// }
// }
// if (change.deletes && Array.isArray(change.deletes)) {
// for (const delKey of change.deletes) {
// await driver.query(dbhan, `HDEL "${change.key}" "${delKey}"`);
// }
// }
// } else if (change.type === 'zset') {
// if (change.updates && Array.isArray(change.updates)) {
// for (const update of change.updates) {
// await driver.query(dbhan, `ZADD "${change.key}" ${update.score} "${update.member}"`);
// }
// }
// if (change.inserts && Array.isArray(change.inserts)) {
// for (const insert of change.inserts) {
// await driver.query(dbhan, `ZADD "${change.key}" ${insert.score} "${insert.member}"`);
// }
// }
// if (change.deletes && Array.isArray(change.deletes)) {
// for (const delMember of change.deletes) {
// await driver.query(dbhan, `ZREM "${change.key}" "${delMember}"`);
// }
// }
// } else if (change.type === 'list') {
// if (change.updates && Array.isArray(change.updates)) {
// for (const update of change.updates) {
// await driver.query(dbhan, `LSET "${change.key}" ${update.index} "${update.value}"`);
// }
// }
// if (change.inserts && Array.isArray(change.inserts)) {
// for (const insert of change.inserts) {
// await driver.query(dbhan, `RPUSH "${change.key}" "${insert.value}"`);
// }
// }
// } else if (change.type === 'set') {
// if (change.inserts && Array.isArray(change.inserts)) {
// for (const insert of change.inserts) {
// await driver.query(dbhan, `SADD "${change.key}" "${insert.value}"`);
// }
// }
// if (change.deletes && Array.isArray(change.deletes)) {
// for (const delValue of change.deletes) {
// await driver.query(dbhan, `SREM "${change.key}" "${delValue}"`);
// }
// }
// } else if (change.type === 'stream') {
// if (change.inserts && Array.isArray(change.inserts)) {
// for (const insert of change.inserts) {
// const streamId = insert.id === '*' || !insert.id ? '*' : insert.id;
// await driver.query(dbhan, `XADD "${change.key}" ${streamId} value "${insert.value}"`);
// }
// }
// if (change.deletes && Array.isArray(change.deletes)) {
// for (const delId of change.deletes) {
// await driver.query(dbhan, `XDEL "${change.key}" "${delId}"`);
// }
// }
// }
// }
process.send({ msgtype: 'response', msgid });
} catch (err) {
process.send({
msgtype: 'response',
msgid,
errorMessage: extractErrorMessage(err, 'Error saving Redis data'),
});
}
}
async function handleSqlPreview({ msgid, objects, options }) {
await waitStructure();
const driver = requireEngineDriver(storedConnection);
@@ -501,6 +602,7 @@ const messageHandlers = {
schemaList: handleSchemaList,
executeSessionQuery: handleExecuteSessionQuery,
evalJsonScript: handleEvalJsonScript,
multiCallMethod: handleMultiCallMethod,
// runCommand: handleRunCommand,
};

View File

@@ -1,6 +1,7 @@
const connectProcess = require('./connectProcess');
const databaseConnectionProcess = require('./databaseConnectionProcess');
const serverConnectionProcess = require('./serverConnectionProcess');
const restConnectionProcess = require('./restConnectionProcess');
const sessionProcess = require('./sessionProcess');
const jslDatastoreProcess = require('./jslDatastoreProcess');
const sshForwardProcess = require('./sshForwardProcess');
@@ -9,6 +10,7 @@ module.exports = {
connectProcess,
databaseConnectionProcess,
serverConnectionProcess,
restConnectionProcess,
sessionProcess,
jslDatastoreProcess,
sshForwardProcess,

View File

@@ -0,0 +1,7 @@
const childProcessChecker = require('../utility/childProcessChecker');
function start() {
childProcessChecker();
}
module.exports = { start };

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