1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

..

136 Commits

Author SHA1 Message Date
861b704344 chore(release): 0.7.20-beta.1 2023-12-02 14:22:32 +01:00
9ce53165e8 chore: post merge cleanup 2023-12-02 14:21:34 +01:00
62614dceb9 Merge pull request #727 from antares-sql/flatpak-experiments
Flatpak experiments
2023-12-02 14:12:51 +01:00
8774dd44e6 Merge branch 'develop' into flatpak-experiments 2023-12-02 14:12:18 +01:00
f0ae01ca5e Merge branch 'develop' of https://github.com/antares-sql/antares into develop 2023-12-02 12:11:35 +01:00
03be777c2a refactor: worker threads to import sql dump instead of process 2023-12-02 11:35:20 +01:00
45a695ac0a refactor: improvements in worker implementation 2023-12-02 11:21:48 +01:00
c176841b75 refactor: worker threads to export sql dump instead of process 2023-12-02 09:31:54 +01:00
329246e2d8 refactor: minor refactor 2023-12-01 20:05:30 +01:00
e26809f260 fix(Flatpak): import/export schema not working 2023-12-01 14:18:40 +01:00
f13d4e6dce feat: copy element names on sidebar from context menu, closes #718 2023-11-29 18:15:22 +01:00
879de91516 refactor: minor refactor 2023-11-27 18:35:56 +01:00
38b32bfb28 build: process.exit on devtoolsInstaller 2023-11-27 13:37:24 +01:00
315d9d84c2 feat: logging errors on log file 2023-11-27 13:36:56 +01:00
c3d96cb35b refactor(Flatpak): temporarily disable import/export feature 2023-11-26 17:49:12 +01:00
05bd7672e1 chore: update package-lock.json 2023-11-26 17:28:45 +01:00
390bf88bb8 refactor: inport/export change for flatpak 2023-11-26 16:42:30 +01:00
984aa893d3 refactor: temporary disable windows process validation 2023-11-24 10:18:44 +01:00
1ac816eaa9 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-11-16 18:30:05 +01:00
6f25fcbc05 ci: update workflow files 2023-11-16 18:16:41 +01:00
bc44465132 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-11-16 18:09:51 +01:00
634a442213 ci: update workflow files 2023-11-16 18:01:40 +01:00
93fe28d07d chore(release): 0.7.20-beta.0 2023-11-15 21:36:27 +01:00
77a78078b2 chore(deps): update electron-builder 2023-11-14 15:14:59 +01:00
0da31254a9 Merge branch 'master' of https://github.com/antares-sql/antares into beta 2023-11-14 14:36:50 +01:00
b1aeabf2b6 ci: update gh actions scripts 2023-11-14 14:36:31 +01:00
f1c857fca9 chore(deps): update better-sqlite3 2023-11-14 10:38:26 +01:00
169f610b2e fix: error with multiple sessions in non-dev environment 2023-11-13 18:08:29 +01:00
075f542dc8 feat: ability to open multiple app sessions 2023-11-12 18:05:11 +01:00
664b2181be Merge pull request #712 from 64knl/feat/update-nl-translation
feat: Update Dutch translation
2023-11-12 15:43:15 +01:00
1bc95b0c2c fix: missing open folder icon for trigger, function and other database elements on sidebar 2023-11-10 18:12:07 +01:00
Rene
8628711374 feat: nl string updates 2023-11-10 15:21:22 +01:00
186fc18363 fix(Firebird SQL): error "Cannot read properties of null" connecting to some databases, fixes #708 2023-11-09 18:49:56 +01:00
e14302bdc0 feat(translation): add Ukrainian language, thanks to #707 2023-11-06 18:01:19 +01:00
13afc8bffd Merge pull request #710 from antares-sql/all-contributors/add-zvlad
docs: add zvlad as a contributor for translation
2023-11-06 09:15:12 +01:00
allcontributors[bot]
811be75c73 docs: update .all-contributorsrc [skip ci] 2023-11-06 08:14:38 +00:00
allcontributors[bot]
bddec52b40 docs: update README.md [skip ci] 2023-11-06 08:14:37 +00:00
e34af5bfa4 chore: update README.md 2023-11-03 18:26:58 +01:00
3322a06fed Merge pull request #703 from jimcat8/cn_main
Updated zh-CN.ts file
2023-11-03 13:17:19 +01:00
tianci
74040cee44 update 2023-11-02 17:52:13 +08:00
tianci
27c904e7f4 Updated zh-CN.ts file 2023-11-02 16:51:13 +08:00
6876dd6063 chore: add flathub badge in README.md 2023-11-02 09:25:13 +01:00
4d676a41cd chode: add flatpak assets 2023-11-01 12:12:14 +01:00
19d9f56f08 chore(release): 0.7.19 2023-11-01 09:43:50 +01:00
4c89578188 Merge branch 'develop' of https://github.com/antares-sql/antares into beta 2023-11-01 09:42:59 +01:00
f6fb266771 fix: table field changes not saved on text fields if pressing enter on textarea modal 2023-10-31 17:57:58 +01:00
3e739bcaa2 chore(release): 0.7.19-beta.2 2023-10-29 11:47:08 +01:00
debc1da289 fix: ssh tunnel keep-alive not working properly 2023-10-28 19:09:19 +02:00
3c2e2be40f chore: regenerate package-lock.json 2023-10-28 18:47:06 +02:00
581ec6a25d Merge branch 'develop' of https://github.com/antares-sql/antares into develop 2023-10-28 18:25:03 +02:00
d30a978cd6 refactor: replace ssh2-promise with @fabio286/ssh2-promise 2023-10-28 18:25:01 +02:00
0015f2e860 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-10-28 11:58:06 +02:00
389e6624d8 chore: update stylelint dependencies 2023-10-28 11:55:18 +02:00
275344eb8b chore(release): 0.7.19-beta.1 2023-10-26 01:07:15 +02:00
cf24adf99e Merge branch 'develop' of https://github.com/antares-sql/antares into beta 2023-10-26 01:06:54 +02:00
2eae580e18 chore: update electron 2023-10-22 19:04:31 +02:00
e4eb27d503 feat(MySQL): RLIKE and NOT RLIKE regular expression filters, closes #688 2023-10-18 18:08:01 +02:00
580973fd04 chore(release): 0.7.19-beta.0 2023-10-14 15:04:00 +02:00
215ab783ab chore: email change 2023-10-14 15:03:09 +02:00
72148e991c Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-10-13 09:23:41 +02:00
6e4c16741a chore: Update CODE_OF_CONDUCT.md 2023-10-13 09:22:04 +02:00
9f9c63bfcc feat: "now" and "random" options added in datetime related data in insert rows tool, closes #402 2023-10-10 18:36:35 +02:00
c0dcf30e73 fix: IN and NOT IN filters not working properly, fixes #687 2023-10-09 18:13:30 +02:00
ddd290c903 fix: timeout issue on long time sql import 2023-10-03 18:19:16 +02:00
267c017921 chore(release): 0.7.18 2023-10-03 09:31:58 +02:00
b3b698b3a2 fix: hotfix for Microsoft Store unauthorized process 2023-10-03 09:31:26 +02:00
71ac3a5164 chore(release): 0.7.17 2023-09-30 16:46:49 +02:00
6fc4418c02 chore(release): 0.7.17-beta.2 2023-09-28 18:11:10 +02:00
b37781df84 fix(UI): small icons in foreign key modal 2023-09-26 18:31:58 +02:00
9c66fd51cb feat: add NOT LIKE to table filters, closes #672 2023-09-25 18:16:07 +02:00
98c1f43a4d refactor: removed icon fonts in ace editor 2023-09-25 18:14:21 +02:00
12825c69d4 Merge branch 'beta' of https://github.com/antares-sql/antares into develop 2023-09-24 23:06:01 +02:00
198ff0103b fix: nsis updater not working 2023-09-24 23:04:35 +02:00
94ce615fc8 chore(release): 0.7.17-beta.1 2023-09-23 16:00:52 +02:00
354928e302 fix(UI): small icons in sidebar elements with long name 2023-09-21 11:18:00 +02:00
6dceaf09be fix(SQLite): table content not refresh after an update, fixes #665 2023-09-20 17:54:25 +02:00
b321e64b83 fix(SQLite): field length lost when editing a table, fixes #664 2023-09-19 18:12:49 +02:00
7e36260cdb fix: table filter not working when search string on integer field, #671 2023-09-19 18:10:27 +02:00
894087e196 Merge branch 'beta' of https://github.com/antares-sql/antares into develop 2023-09-18 15:13:03 +02:00
922f56f69b fix: flip not working on BaseIcon component 2023-09-18 08:56:26 +02:00
7d84b4e81f chore(release): 0.7.17-beta.0 2023-09-17 23:56:24 +02:00
495e48625c ci: manual creation of arm64 linux artifacts 2023-09-17 23:56:05 +02:00
5934d3a990 ci: update beta build node version 2023-09-17 19:59:13 +02:00
cb92ca99f6 refactor: minor refactor 2023-09-17 19:04:24 +02:00
e7bec0aaaf perf: migration from font icons to svg icons 2023-09-17 18:49:37 +02:00
9de5f67d18 perf: improved ipc validation on Linux 2023-09-13 16:30:08 +02:00
13592425af perf(core): improved app security, fixes #666 2023-09-13 14:21:31 +02:00
0de5ef8a98 fix: empty workspace deleting connections in some conditions 2023-09-11 18:02:19 +02:00
785bc40ad0 build(deps): update some dependencies (vue, pinia, electron) 2023-09-08 15:05:28 +02:00
940f64e6ab refactor: small cleanup 2023-09-08 09:20:13 +02:00
535dd21d69 Merge pull request #662 from 64knl/feat/update-dutch-translation
feat(translation): Update Dutch
2023-09-07 15:50:04 +02:00
Rene
b43c4000d5 feat(translation): Update Dutch 2023-09-07 10:44:38 +02:00
757a2b3cbf fix(SQLite): disconnecting a sqlite connection does not truly close it, fixes #661 2023-09-05 18:08:43 +02:00
c78258219a Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-09-05 17:45:47 +02:00
fe23d86694 Merge pull request #633 from digitalgopnik/feat/ssh-keep-alive-interval
feat: customize keep-alive-interval for ssh-tunnel
2023-09-04 17:12:39 +02:00
8d605ee287 feat: keep alive interval in seconds 2023-09-04 16:27:50 +02:00
fbe271af37 refactor: adaptation for changes to i18n 2023-09-04 15:27:56 +02:00
b838916937 Merge branch 'develop' of https://github.com/antares-sql/antares into pr/digitalgopnik/633 2023-09-04 14:59:21 +02:00
77ab561058 Merge pull request #660 from antares-sql/dependabot/npm_and_yarn/develop/ace-builds-1.24.1
build(deps): bump ace-builds from 1.14.0 to 1.24.1
2023-09-04 14:05:33 +02:00
dependabot[bot]
7bb03b4922 build(deps): bump ace-builds from 1.14.0 to 1.24.1
Bumps [ace-builds](https://github.com/ajaxorg/ace-builds) from 1.14.0 to 1.24.1.
- [Release notes](https://github.com/ajaxorg/ace-builds/releases)
- [Changelog](https://github.com/ajaxorg/ace-builds/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ajaxorg/ace-builds/compare/v1.14.0...v1.24.1)

---
updated-dependencies:
- dependency-name: ace-builds
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 11:53:17 +00:00
de607a6b06 Merge pull request #659 from antares-sql/dependabot/npm_and_yarn/develop/vueuse/core-10.4.1
build(deps): bump @vueuse/core from 8.7.5 to 10.4.1
2023-09-04 13:52:29 +02:00
c5f89f9fdd Merge pull request #640 from antares-sql/dependabot/npm_and_yarn/develop/mdi/font-7.2.96
build(deps): bump @mdi/font from 7.1.96 to 7.2.96
2023-09-04 13:48:41 +02:00
bf46b89988 Merge pull request #658 from antares-sql/dependabot/npm_and_yarn/develop/sql-formatter-13.0.0
build(deps): bump sql-formatter from 12.2.2 to 13.0.0
2023-09-04 13:38:33 +02:00
e6f45d71c7 fix(UI): update notification indicator moves settings icon 2023-09-02 16:09:19 +02:00
dependabot[bot]
8608c27f20 build(deps): bump @vueuse/core from 8.7.5 to 10.4.1
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 8.7.5 to 10.4.1.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v10.4.1/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-01 19:14:14 +00:00
dependabot[bot]
e031c75e28 build(deps): bump sql-formatter from 12.2.2 to 13.0.0
Bumps [sql-formatter](https://github.com/sql-formatter-org/sql-formatter) from 12.2.2 to 13.0.0.
- [Release notes](https://github.com/sql-formatter-org/sql-formatter/releases)
- [Changelog](https://github.com/sql-formatter-org/sql-formatter/blob/master/.release-it.json)
- [Commits](https://github.com/sql-formatter-org/sql-formatter/compare/v12.2.2...v13.0.0)

---
updated-dependencies:
- dependency-name: sql-formatter
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-01 19:13:36 +00:00
c1891dd5de chore(release): 0.7.16 2023-08-26 15:21:02 +02:00
53fe986bcb Merge branch 'master' of https://github.com/antares-sql/antares into beta 2023-08-26 15:19:45 +02:00
661043b181 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2023-08-26 15:14:10 +02:00
b74ec71b10 chore: mastodon social link 2023-08-18 16:05:04 +02:00
54df0e4aa8 style: implement eslint simple-import-sort 2023-08-18 15:57:31 +02:00
d702ce3fa9 chore(release): 0.7.16-beta.1 2023-08-18 12:22:56 +02:00
63544e95da Merge branch 'feat/export-connections' of https://github.com/antares-sql/antares into develop 2023-08-18 12:12:05 +02:00
6df214558f feat: ability to import connections 2023-08-18 12:11:08 +02:00
f5b86e59e7 feat: ability to export connections 2023-08-17 13:48:55 +02:00
28f14c9195 Merge pull request #653 from antares-sql/all-contributors/add-Lawondyss
docs: add Lawondyss as a contributor for translation
2023-08-14 12:17:38 +02:00
allcontributors[bot]
90922e6f96 docs: update .all-contributorsrc [skip ci] 2023-08-14 10:17:13 +00:00
allcontributors[bot]
4b414df7e4 docs: update README.md [skip ci] 2023-08-14 10:17:12 +00:00
27f0068415 Merge pull request #652 from Lawondyss/develop
Add CZ translation and titles for buttons
2023-08-14 12:16:44 +02:00
Ladislav Vondráček
52e42fa1b5 fix: add some missing titles of buttons 2023-08-14 11:50:38 +02:00
Ladislav Vondráček
5b9b539bc7 chore(i18n): add czech translation 2023-08-14 11:47:41 +02:00
832aa75ebe fix(SQLite): improved view body parsing 2023-08-14 10:44:36 +02:00
aba67f3872 refactor(translation): refactor ca-ES to new format 2023-08-14 09:16:40 +02:00
5a1644f023 Merge branches 'develop' and 'master' of https://github.com/antares-sql/antares into develop 2023-08-14 09:07:55 +02:00
e372712556 Merge pull request #651 from antares-sql/all-contributors/add-markusand
docs: add markusand as a contributor for translation
2023-08-14 09:06:55 +02:00
d4727a7d20 Merge pull request #649 from markusand/master
feat: Add support for catalan language
2023-08-14 09:06:42 +02:00
allcontributors[bot]
f2c2f33afa docs: update .all-contributorsrc [skip ci] 2023-08-14 07:06:40 +00:00
allcontributors[bot]
6dc4bdd0c0 docs: update README.md [skip ci] 2023-08-14 07:06:39 +00:00
Marc Vilella
9c6f1a9ea5 feat: Add catalan language 2023-08-13 19:20:26 +02:00
dd264f802e ci: update snapcraft.yaml 2023-08-11 18:12:38 +02:00
fed0e10702 Merge branch 'master' of https://github.com/antares-sql/antares into beta 2023-08-11 17:34:50 +02:00
e7ce4ef6ed Merge branch 'master' of https://github.com/antares-sql/antares 2023-08-11 17:34:30 +02:00
e03f2eef49 ci: update snapcraft.yaml 2023-08-11 17:34:27 +02:00
Christian Ratz
c0c33f8237 Merge branch 'develop' into feat/ssh-keep-alive-interval 2023-08-04 11:12:48 +02:00
Christian Ratz
b4731d67a5 feat: customize keep-alive-interval for ssh-tunnel 2023-08-04 11:06:35 +02:00
dependabot[bot]
de40414a0d build(deps): bump @mdi/font from 7.1.96 to 7.2.96
Bumps [@mdi/font](https://github.com/Templarian/MaterialDesign-Webfont) from 7.1.96 to 7.2.96.
- [Commits](https://github.com/Templarian/MaterialDesign-Webfont/compare/v7.1.96...v7.2.96)

---
updated-dependencies:
- dependency-name: "@mdi/font"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 08:55:49 +00:00
210 changed files with 24937 additions and 5071 deletions

View File

@@ -239,6 +239,33 @@
"contributions": [
"platform"
]
},
{
"login": "markusand",
"name": "Marc Vilella",
"avatar_url": "https://avatars.githubusercontent.com/u/12972543?v=4",
"profile": "https://github.com/markusand",
"contributions": [
"translation"
]
},
{
"login": "Lawondyss",
"name": "Ladislav Vondráček",
"avatar_url": "https://avatars.githubusercontent.com/u/272130?v=4",
"profile": "https://github.com/Lawondyss",
"contributions": [
"translation"
]
},
{
"login": "zvlad",
"name": "Vladyslav",
"avatar_url": "https://avatars.githubusercontent.com/u/9055134?v=4",
"profile": "https://github.com/zvlad",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 7,

View File

@@ -18,7 +18,8 @@
},
"plugins": [
"vue",
"@typescript-eslint"
"@typescript-eslint",
"simple-import-sort"
],
"rules": {
"space-infix-ops": "off",
@@ -91,6 +92,8 @@
}
}
],
"@typescript-eslint/no-var-requires": "off"
"@typescript-eslint/no-var-requires": "off",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
}
}

View File

@@ -13,8 +13,9 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-11, ubuntu-latest, windows-latest]
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- name: Check out Git repository
@@ -25,7 +26,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 20
- name: Install dependencies
run: npm i

View File

@@ -13,8 +13,9 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-11, ubuntu-latest, windows-latest]
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- name: Exit if not on master branch
@@ -31,7 +32,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 20
- name: Install dependencies
run: npm i

View File

@@ -9,11 +9,13 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@v3
with:
ref: master
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
run: npm i

View File

@@ -5,7 +5,7 @@ on:
jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
@@ -13,7 +13,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 20
- name: Install dependencies
run: npm i

View File

@@ -13,7 +13,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 20
- name: npm install & build
run: |
@@ -29,3 +29,30 @@ jobs:
build
!build/*-unpacked
!build/.icon-ico
build-beta:
runs-on: macos-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
with:
ref: beta
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: npm install & build
run: |
npm install
npm run build
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: macos-build-beta
retention-days: 3
path: |
build
!build/*-unpacked
!build/.icon-ico

33
.github/workflows/test-builds.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Test build [DEVELOP]
on:
workflow_dispatch: {}
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
jobs:
release:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- name: Check out Git repository
uses: actions/checkout@v3
with:
ref: develop
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install dependencies
run: npm i
- name: "Build"
run: npm run build

BIN
.nvmrc Normal file

Binary file not shown.

View File

@@ -10,7 +10,8 @@
"translation",
"Linux",
"MacOS",
"deps"
"deps",
"Flatpak"
],
"svg.preview.background": "transparent"
}

View File

@@ -2,6 +2,142 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.20-beta.1](https://github.com/antares-sql/antares/compare/v0.7.20-beta.0...v0.7.20-beta.1) (2023-12-02)
### Features
* copy element names on sidebar from context menu, closes [#718](https://github.com/antares-sql/antares/issues/718) ([f13d4e6](https://github.com/antares-sql/antares/commit/f13d4e6dceb70b0c7cb8d56ddfb5f00e938571cc))
* logging errors on log file ([315d9d8](https://github.com/antares-sql/antares/commit/315d9d84c2caa29852d68bd189750b2a4028d953))
### Bug Fixes
* **Flatpak:** import/export schema not working ([e26809f](https://github.com/antares-sql/antares/commit/e26809f260099ba194bf5d00671cae14d438197b))
### [0.7.20-beta.0](https://github.com/antares-sql/antares/compare/v0.7.19...v0.7.20-beta.0) (2023-11-15)
### Features
* ability to open multiple app sessions ([075f542](https://github.com/antares-sql/antares/commit/075f542dc8f4a48bef07b86b78c40d03fcdccc56))
* nl string updates ([8628711](https://github.com/antares-sql/antares/commit/8628711374269c29c4b1e6722fe66b0d8179477e))
* **translation:** add Ukrainian language, thanks to [#707](https://github.com/antares-sql/antares/issues/707) ([e14302b](https://github.com/antares-sql/antares/commit/e14302bdc0038b84a1a06089753205149cd1a92b))
### Bug Fixes
* error with multiple sessions in non-dev environment ([169f610](https://github.com/antares-sql/antares/commit/169f610b2ee4857661ec3da7f04b628fec21f1f0))
* **Firebird SQL:** error "Cannot read properties of null" connecting to some databases, fixes [#708](https://github.com/antares-sql/antares/issues/708) ([186fc18](https://github.com/antares-sql/antares/commit/186fc18363b6f14678465a8e38d85b1319e47b50))
* missing open folder icon for trigger, function and other database elements on sidebar ([1bc95b0](https://github.com/antares-sql/antares/commit/1bc95b0c2cd91bbf0410a23266e23bbbf2a71995))
### [0.7.19](https://github.com/antares-sql/antares/compare/v0.7.19-beta.2...v0.7.19) (2023-11-01)
### Bug Fixes
* table field changes not saved on text fields if pressing enter on textarea modal ([f6fb266](https://github.com/antares-sql/antares/commit/f6fb266771f2d798c8ae42b997c1e33520cf21c3))
### [0.7.19-beta.2](https://github.com/antares-sql/antares/compare/v0.7.19-beta.1...v0.7.19-beta.2) (2023-10-29)
### Bug Fixes
* ssh tunnel keep-alive not working properly ([debc1da](https://github.com/antares-sql/antares/commit/debc1da289d5e35d59adf69d094b329cf93af536))
### [0.7.19-beta.1](https://github.com/antares-sql/antares/compare/v0.7.19-beta.0...v0.7.19-beta.1) (2023-10-25)
### Features
* **MySQL:** RLIKE and NOT RLIKE regular expression filters, closes [#688](https://github.com/antares-sql/antares/issues/688) ([e4eb27d](https://github.com/antares-sql/antares/commit/e4eb27d503e8f912178359c01c62a9b523d17848))
### [0.7.19-beta.0](https://github.com/antares-sql/antares/compare/v0.7.18...v0.7.19-beta.0) (2023-10-14)
### Features
* "now" and "random" options added in datetime related data in insert rows tool, closes [#402](https://github.com/antares-sql/antares/issues/402) ([9f9c63b](https://github.com/antares-sql/antares/commit/9f9c63bfcc3423bfeef143cd835f48c62900a799))
### Bug Fixes
* IN and NOT IN filters not working properly, fixes [#687](https://github.com/antares-sql/antares/issues/687) ([c0dcf30](https://github.com/antares-sql/antares/commit/c0dcf30e73a69b25b01ba31d21b27c1983ed2db6))
* timeout issue on long time sql import ([ddd290c](https://github.com/antares-sql/antares/commit/ddd290c90344241eaa70cb528552e942fd7edec0))
### [0.7.18](https://github.com/antares-sql/antares/compare/v0.7.17...v0.7.18) (2023-10-03)
### Bug Fixes
* hotfix for Microsoft Store unauthorized process ([b3b698b](https://github.com/antares-sql/antares/commit/b3b698b3a23a3c848921ab40fc0fec5d8178ef0e))
### [0.7.17](https://github.com/antares-sql/antares/compare/v0.7.17-beta.2...v0.7.17) (2023-09-30)
### [0.7.17-beta.2](https://github.com/antares-sql/antares/compare/v0.7.17-beta.1...v0.7.17-beta.2) (2023-09-28)
### Features
* add NOT LIKE to table filters, closes [#672](https://github.com/antares-sql/antares/issues/672) ([9c66fd5](https://github.com/antares-sql/antares/commit/9c66fd51cbbe6f21a1fa6a34cc962496d3db7a98))
### Bug Fixes
* nsis updater not working ([198ff01](https://github.com/antares-sql/antares/commit/198ff0103bfa95e3491296d352c944165f31b87e))
* **UI:** small icons in foreign key modal ([b37781d](https://github.com/antares-sql/antares/commit/b37781df84cf7ee99a69ecaa54480d662d79c4aa))
### [0.7.17-beta.1](https://github.com/antares-sql/antares/compare/v0.7.17-beta.0...v0.7.17-beta.1) (2023-09-23)
### Bug Fixes
* flip not working on BaseIcon component ([922f56f](https://github.com/antares-sql/antares/commit/922f56f69b168302a9d1ff86565d3f09400d6a7c))
* **SQLite:** field length lost when editing a table, fixes [#664](https://github.com/antares-sql/antares/issues/664) ([b321e64](https://github.com/antares-sql/antares/commit/b321e64b835e0b39da116c4a77bac50247f240f3))
* **SQLite:** table content not refresh after an update, fixes [#665](https://github.com/antares-sql/antares/issues/665) ([6dceaf0](https://github.com/antares-sql/antares/commit/6dceaf09be7bd46f1915721abd03253ffc517256))
* table filter not working when search string on integer field, [#671](https://github.com/antares-sql/antares/issues/671) ([7e36260](https://github.com/antares-sql/antares/commit/7e36260cdb0438197152b5c6ac61db8ae8a9791a))
* **UI:** small icons in sidebar elements with long name ([354928e](https://github.com/antares-sql/antares/commit/354928e302437d608903d1434d99d68eb79aa6e9))
### [0.7.17-beta.0](https://github.com/antares-sql/antares/compare/v0.7.16...v0.7.17-beta.0) (2023-09-17)
### Features
* customize keep-alive-interval for ssh-tunnel ([b4731d6](https://github.com/antares-sql/antares/commit/b4731d67a5b819bf87c437eb626f30a7489477f9))
* keep alive interval in seconds ([8d605ee](https://github.com/antares-sql/antares/commit/8d605ee2872c20495f0ac4de84bf17d61c6455ae))
* **translation:** Update Dutch ([b43c400](https://github.com/antares-sql/antares/commit/b43c4000d5905838348c391f25b3228dc6a6b2ac))
### Bug Fixes
* empty workspace deleting connections in some conditions ([0de5ef8](https://github.com/antares-sql/antares/commit/0de5ef8a985cc8d9f1fa4479eeaf7dbb20f5fd7c))
* **SQLite:** disconnecting a sqlite connection does not truly close it, fixes [#661](https://github.com/antares-sql/antares/issues/661) ([757a2b3](https://github.com/antares-sql/antares/commit/757a2b3cbf0d7f4df37eb5d90faa2f9821794e5f))
* **UI:** update notification indicator moves settings icon ([e6f45d7](https://github.com/antares-sql/antares/commit/e6f45d71c75206187a7ae87e5f90a22d288a53ef))
### Improvements
* **core:** improved app security, fixes [#666](https://github.com/antares-sql/antares/issues/666) ([1359242](https://github.com/antares-sql/antares/commit/13592425afc643fe7188463fe4a2d02b6e27f87c))
* improved ipc validation on Linux ([9de5f67](https://github.com/antares-sql/antares/commit/9de5f67d189c9b56fff41b29e6df69a3909bfa26))
* migration from font icons to svg icons ([e7bec0a](https://github.com/antares-sql/antares/commit/e7bec0aaaf019fe0c3040d2d9443233e46a60188))
### [0.7.16](https://github.com/antares-sql/antares/compare/v0.7.16-beta.1...v0.7.16) (2023-08-26)
### [0.7.16-beta.1](https://github.com/antares-sql/antares/compare/v0.7.16-beta.0...v0.7.16-beta.1) (2023-08-18)
### Features
* ability to export connections ([f5b86e5](https://github.com/antares-sql/antares/commit/f5b86e59e7e41bbbe1555ec8a85ec1f849e8271d))
* ability to import connections ([6df2145](https://github.com/antares-sql/antares/commit/6df214558f8186dd5fbf101505465f4c07c8ad5f))
* Add catalan language ([9c6f1a9](https://github.com/antares-sql/antares/commit/9c6f1a9ea52b4791082bdbae91b764c486684c3b))
### Bug Fixes
* add some missing titles of buttons ([52e42fa](https://github.com/antares-sql/antares/commit/52e42fa1b529512154c0680124c5e425964a5b1a))
* **SQLite:** improved view body parsing ([832aa75](https://github.com/antares-sql/antares/commit/832aa75ebe46776b9c365dde2dd13db3746adcd5))
### [0.7.16-beta.0](https://github.com/antares-sql/antares/compare/v0.7.15...v0.7.16-beta.0) (2023-08-11)

View File

@@ -61,7 +61,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
fabio286@gmail.com.
info@fabiodistasio.it.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View File

@@ -7,8 +7,7 @@
# Antares SQL Client
![GitHub package.json version](https://img.shields.io/github/package-json/v/fabio286/antares) ![GitHub](https://img.shields.io/github/license/fabio286/antares) [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Ffabio286%2Fantares%2Fbadge&style=flat)](https://actions-badge.atrox.dev/fabio286/antares/goto) [![antares](https://snapcraft.io/antares/badge.svg)](https://snapcraft.io/antares) [![antares](https://snapcraft.io/antares/trending.svg?name=0)](https://snapcraft.io/antares)
![Mastodon Follow](https://img.shields.io/mastodon/follow/%20110860460902482117?domain=https%3A%2F%2Ffosstodon.org&style=social) [![Twitter Follow](https://img.shields.io/twitter/follow/AntaresSQL?style=social)](https://twitter.com/AntaresSQL) [![Plant a Tree](https://raw.githubusercontent.com/Fabio286/treedom-badge/master/svg/plant-a-tree.svg)](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
![GitHub package.json version](https://img.shields.io/github/package-json/v/fabio286/antares) ![GitHub](https://img.shields.io/github/license/fabio286/antares) [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Ffabio286%2Fantares%2Fbadge&style=flat)](https://actions-badge.atrox.dev/fabio286/antares/goto) ![Mastodon Follow](https://img.shields.io/mastodon/follow/%20110860460902482117?domain=https%3A%2F%2Ffosstodon.org&style=social) [![Twitter Follow](https://img.shields.io/twitter/follow/AntaresSQL?style=social)](https://twitter.com/AntaresSQL) [![Plant a Tree](https://raw.githubusercontent.com/Fabio286/treedom-badge/master/svg/plant-a-tree.svg)](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
Our target is to support as many databases as possible, and all major operating systems, including the ARM versions.
@@ -21,7 +20,8 @@ We are actively working on it, hoping to provide new cool features, improvements
👁 To stay tuned for new releases follow Antares SQL on [Mastodon](https://fosstodon.org/@AntaresSQL) or [Twitter](https://twitter.com/AntaresSQL).
🌟 Don't forget to **leave a star** if you appreciate this project.
🗳️ Polls:
🗳️ Polls:
- **[Which is the main OS you use Antares on?](https://github.com/antares-sql/antares/discussions/379)**
- **[Which database do you use the most?](https://github.com/antares-sql/antares/discussions/594)**
@@ -55,7 +55,7 @@ Since Antares SQL is a free software we don't have a budget to spend on annual l
### Linux
On Linux you can simply download and run the `.AppImage` distribution, install from Snap Store, from AUR or from our [PPA repository](https://github.com/antares-sql/antares-ppa).
On Linux you can simply download and run the `.AppImage` distribution, install from FlatHub, Snap Store, AUR or from our [PPA repository](https://github.com/antares-sql/antares-ppa).
### Windows
@@ -67,8 +67,8 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
## Download
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/antares) [![Get it from AUR](https://raw.githubusercontent.com/Fabio286/antares/3e00c4bae6e036300c752c1a40c5a038fea9c169/docs/aur-badge.svg)](https://aur.archlinux.org/packages/antares-sql-bin) [<img src="https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png" style="height: 56px">](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
[<img height='56' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.svg'/>](https://flathub.org/apps/it.fabiodistasio.AntaresSQL) [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/antares) [![Get it from AUR](https://raw.githubusercontent.com/antares-sql/antares/master/docs/aur-badge.svg)](https://aur.archlinux.org/packages/antares-sql-bin) [<img src="https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png" style="height: 56px">](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
🚀 **[Other Downloads](https://github.com/antares-sql/antares/releases/latest)**
## Coming soon
@@ -153,6 +153,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/m1khal3v"><img src="https://avatars.githubusercontent.com/u/41085561?v=4?s=100" width="100px;" alt="Anton Mikhalev"/><br /><sub><b>Anton Mikhalev</b></sub></a><br /><a href="#translation-m1khal3v" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://64k.nl/"><img src="https://avatars.githubusercontent.com/u/3864423?v=4?s=100" width="100px;" alt="René"/><br /><sub><b>René</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=64knl" title="Code">💻</a> <a href="#translation-64knl" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zxp19821005"><img src="https://avatars.githubusercontent.com/u/4915850?v=4?s=100" width="100px;" alt="Woodenman"/><br /><sub><b>Woodenman</b></sub></a><br /><a href="#platform-zxp19821005" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/markusand"><img src="https://avatars.githubusercontent.com/u/12972543?v=4?s=100" width="100px;" alt="Marc Vilella"/><br /><sub><b>Marc Vilella</b></sub></a><br /><a href="#translation-markusand" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Lawondyss"><img src="https://avatars.githubusercontent.com/u/272130?v=4?s=100" width="100px;" alt="Ladislav Vondráček"/><br /><sub><b>Ladislav Vondráček</b></sub></a><br /><a href="#translation-Lawondyss" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zvlad"><img src="https://avatars.githubusercontent.com/u/9055134?v=4?s=100" width="100px;" alt="Vladyslav"/><br /><sub><b>Vladyslav</b></sub></a><br /><a href="#translation-zvlad" title="Translation">🌍</a></td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,9 @@
[Desktop Entry]
Name=Antares SQL
Exec=startantares
Terminal=false
Type=Application
Icon=it.fabiodistasio.AntaresSQL
StartupWMClass=antares
Comment=A modern, fast and productivity driven SQL client with a focus in UX
Categories=Development;

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>it.fabiodistasio.AntaresSQL</id>
<name>Antares SQL</name>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<developer_name>Fabio Di Stasio</developer_name>
<summary>A modern, fast and productivity driven SQL client with a focus in UX</summary>
<url type="homepage">https://antares-sql.app/</url>
<url type="bugtracker">https://github.com/antares-sql/antares/issues</url>
<url type="help">https://github.com/antares-sql/antares/discussions</url>
<url type="donation">https://paypal.me/fabiodistasio</url>
<description>
<p>Antares is an SQL client that aims to become an useful and complete tool, especially for developers. </p>
<p>The main goal is to develop a totally free, full featured, cross platform and open source alternative.
A modern application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenu; productivity comes first.</p>
<p>Supported database: </p>
<ul>
<li>MySQL/MariaDB</li>
<li>PostgreSQL</li>
<li>SQLite</li>
<li>Firebird SQL</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<image type="source">https://lh3.googleusercontent.com/drive-viewer/AK7aPaC00fbmJIUcfwSPv-hjoxEmHS8NapR8qyOqOpopMIdcDFqYKNDs5mdIK08hnhZdHMrozTfR4Hx3Yj6bQ0zgfStEEFhxWg=s1600</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1" />
<releases>
<release version="v0.7.19" date="2023-11-01"/>
</releases>
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -1,76 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate" viewBox="0 0 182 56" width="182px" height="56px">
<svg xmlns="http://www.w3.org/2000/svg" style="isolation:isolate" width="182" height="56">
<defs>
<clipPath id="_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF">
<rect width="182" height="56" />
<clipPath id="prefix__a">
<path d="M0 0h182v56H0z" />
</clipPath>
</defs>
<g clip-path="url(#_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF)">
<path
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
style="stroke:none;fill:#252525;stroke-miterlimit:10;" />
<path
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
style="fill:none;stroke:#CDCDCD;stroke-width:1;stroke-miterlimit:2;" />
<g>
<g>
<path
d=" M 60.898 13.777 C 58.555 13.774 56.61 14.254 55.858 14.516 L 55.083 18.697 C 55.081 18.712 58.937 17.669 60.635 17.73 C 63.447 17.831 63.706 18.805 63.656 20.119 C 63.704 20.196 62.931 18.931 60.498 18.889 C 57.43 18.836 53.098 19.976 53.104 24.608 C 53.022 29.818 56.997 31.351 59.704 31.379 C 62.138 31.335 63.279 30.458 63.904 29.988 C 64.726 29.129 65.665 28.265 66.561 27.229 C 65.714 28.77 64.978 29.835 64.213 30.651 L 64.213 31.339 L 67.913 30.716 L 67.938 20.66 C 67.901 19.237 68.754 13.791 60.898 13.777 Z M 60.367 22.533 C 61.9 22.554 63.659 23.31 63.662 25.129 C 63.669 26.784 61.589 27.674 60.235 27.66 C 58.881 27.646 57.085 26.596 57.077 24.982 C 57.103 23.54 58.771 22.496 60.367 22.533 Z "
fill-rule="evenodd" fill="rgb(255,255,255)" />
<path
d=" M 70.378 14.707 L 70.352 31.36 L 74.662 30.529 L 74.67 21.087 C 74.671 19.681 76.679 18.039 79.198 18.065 C 79.733 17.097 80.738 14.625 80.983 14.062 C 75.354 14.049 75.282 15.68 74.303 16.483 C 74.293 14.952 74.3 14.033 74.3 14.033 L 70.378 14.707 L 70.378 14.707 Z "
fill-rule="evenodd" fill="rgb(255,255,255)" />
<path
d=" M 94.632 16.893 C 94.591 16.873 92.385 14.312 87.949 14.292 C 83.795 14.223 79.135 15.834 79.061 22.8 C 79.097 28.925 83.537 31.318 87.973 31.365 C 92.72 31.414 94.609 28.396 94.722 28.322 C 94.156 27.83 92.034 25.728 92.034 25.728 C 92.034 25.728 90.709 27.615 88.138 27.639 C 85.566 27.664 83.331 25.651 83.299 22.844 C 83.266 20.036 85.354 18.515 88.157 18.392 C 90.584 18.392 91.984 19.959 91.984 19.959 L 94.632 16.893 L 94.632 16.893 Z "
fill-rule="evenodd" fill="rgb(255,255,255)" />
<path
d=" M 100.065 8.879 L 95.996 9.835 L 96.026 31.526 L 100.034 30.802 L 100.08 20.595 C 100.089 19.524 101.628 17.88 104.2 17.933 C 106.658 17.958 107.207 19.571 107.201 19.775 L 107.272 31.592 L 111.224 30.894 L 111.239 18.363 C 111.265 17.157 108.598 14.61 104.311 14.592 C 102.273 14.596 101.145 15.057 100.571 15.397 C 99.588 16.156 98.466 16.883 97.362 17.811 C 98.382 16.501 99.239 15.595 100.075 14.921 L 100.065 8.879 L 100.065 8.879 Z "
fill-rule="evenodd" fill="rgb(255,255,255)" />
</g>
<g>
<path
d=" M 114.673 9.441 L 116.508 8.982 L 116.595 30.85 L 114.73 31.168 L 114.673 9.441 L 114.673 9.441 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
<path
d=" M 119.663 15.968 L 121.271 15.252 L 121.285 30.932 L 119.731 31.253 L 119.663 15.968 L 119.663 15.968 Z M 119.28 10.314 L 120.577 9.255 L 121.655 10.454 L 120.357 11.54 L 119.28 10.314 L 119.28 10.314 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
<path
d=" M 124.296 15.682 L 126.131 15.308 L 126.14 18.586 C 126.14 18.727 127.148 14.924 132.008 15.009 C 136.727 15.035 137.499 18.688 137.473 19.507 L 137.531 31.034 L 135.922 31.384 L 135.913 19.998 C 135.932 19.665 135.178 16.853 131.843 16.843 C 128.509 16.833 126.199 19.265 126.203 20.818 L 126.229 30.848 L 124.365 31.335 L 124.296 15.682 L 124.296 15.682 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
<path
d=" M 153.547 31.117 L 151.711 31.492 L 151.703 28.214 C 151.703 28.073 150.694 31.876 145.835 31.791 C 141.116 31.765 140.344 28.112 140.37 27.293 L 140.311 15.765 L 142.261 15.372 L 142.292 26.758 C 142.292 27.069 142.665 29.947 145.999 29.957 C 149.334 29.967 151.669 27.949 151.686 24.911 L 151.662 15.928 L 153.477 15.464 L 153.547 31.117 L 153.547 31.117 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
<path
d=" M 157.144 15.553 L 155.857 16.56 L 160.792 23.018 L 155.529 30.478 L 156.894 31.492 L 161.841 24.563 L 166.948 31.656 L 168.211 30.649 L 162.738 23.065 L 167.104 16.933 L 165.762 15.797 L 161.785 21.472 L 157.144 15.553 L 157.144 15.553 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
</g>
<g clip-path="url(#prefix__a)">
<!-- <path d="M2.5.5h178a2 2 0 012 2v52a2 2 0 01-2 2H2.5a2 2 0" fill="#252525" stroke="#FFF" stroke-width="2"/> -->
<rect x="0" y="0" width="182" height="56" fill="#252525" stroke="#FFF" stroke-width="1" rx="0" />
<g fill-rule="evenodd" fill="#FFF">
<path
d=" M 33.112 3.879 C 30.965 9.143 29.67 12.587 27.279 17.695 C 28.745 19.248 30.544 21.058 33.466 23.101 C 30.325 21.809 28.182 20.511 26.581 19.164 C 23.521 25.549 18.728 34.643 9 52.121 C 16.645 47.707 22.572 44.986 28.095 43.948 C 27.858 42.928 27.723 41.824 27.733 40.673 L 27.742 40.428 C 27.863 35.53 30.411 31.763 33.43 32.019 C 36.448 32.274 38.794 36.455 38.673 41.353 C 38.65 42.275 38.546 43.162 38.364 43.984 C 43.828 45.053 49.691 47.767 57.233 52.121 C 55.746 49.383 54.419 46.915 53.151 44.565 C 51.154 43.017 49.072 41.003 44.823 38.822 C 47.743 39.581 49.834 40.456 51.464 41.435 C 38.575 17.439 37.532 14.251 33.112 3.879 Z "
fill-rule="evenodd" fill="rgb(23,147,209)" />
<g>
<path
d=" M 170.614 30.156 L 170.614 28.802 L 170.109 28.802 L 170.109 28.621 L 171.325 28.621 L 171.325 28.802 L 170.817 28.802 L 170.817 30.156 L 170.614 30.156 Z "
fill="rgb(23,147,209)" />
<path
d=" M 171.536 30.156 L 171.536 28.621 L 171.842 28.621 L 172.205 29.708 C 172.238 29.809 172.263 29.884 172.278 29.935 C 172.295 29.879 172.323 29.797 172.36 29.689 L 172.727 28.621 L 173 28.621 L 173 30.156 L 172.804 30.156 L 172.804 28.871 L 172.358 30.156 L 172.175 30.156 L 171.732 28.849 L 171.732 30.156 L 171.536 30.156 Z "
fill="rgb(23,147,209)" />
</g>
<g>
<path
d=" M 57.471 47.815 L 57.471 46.493 L 56.977 46.493 L 56.977 46.316 L 58.166 46.316 L 58.166 46.493 L 57.67 46.493 L 57.67 47.815 L 57.471 47.815 Z "
fill="rgb(23,147,209)" />
<path
d=" M 58.372 47.815 L 58.372 46.316 L 58.671 46.316 L 59.026 47.377 C 59.059 47.476 59.083 47.55 59.098 47.599 C 59.115 47.545 59.141 47.465 59.177 47.359 L 59.536 46.316 L 59.803 46.316 L 59.803 47.815 L 59.612 47.815 L 59.612 46.56 L 59.176 47.815 L 58.997 47.815 L 58.564 46.539 L 58.564 47.815 L 58.372 47.815"
fill="rgb(23,147,209)" />
</g>
d="M60.898 13.777c-2.343-.003-4.288.477-5.04.739l-.775 4.181c-.002.015 3.854-1.028 5.552-.967 2.812.101 3.071 1.075 3.021 2.389.048.077-.725-1.188-3.158-1.23-3.068-.053-7.4 1.087-7.394 5.719-.082 5.21 3.893 6.743 6.6 6.771 2.434-.044 3.575-.921 4.2-1.391.822-.859 1.761-1.723 2.657-2.759-.847 1.541-1.583 2.606-2.348 3.422v.688l3.7-.623.025-10.056c-.037-1.423.816-6.869-7.04-6.883zm-.531 8.756c1.533.021 3.292.777 3.295 2.596.007 1.655-2.073 2.545-3.427 2.531-1.354-.014-3.15-1.064-3.158-2.678.026-1.442 1.694-2.486 3.29-2.449zM70.378 14.707l-.026 16.653 4.31-.831.008-9.442c.001-1.406 2.009-3.048 4.528-3.022.535-.968 1.54-3.44 1.785-4.003-5.629-.013-5.701 1.618-6.68 2.421-.01-1.531-.003-2.45-.003-2.45l-3.922.674zM94.632 16.893c-.041-.02-2.247-2.581-6.683-2.601-4.154-.069-8.814 1.542-8.888 8.508.036 6.125 4.476 8.518 8.912 8.565 4.747.049 6.636-2.969 6.749-3.043-.566-.492-2.688-2.594-2.688-2.594s-1.325 1.887-3.896 1.911c-2.572.025-4.807-1.988-4.839-4.795-.033-2.808 2.055-4.329 4.858-4.452 2.427 0 3.827 1.567 3.827 1.567l2.648-3.066zM100.065 8.879l-4.069.956.03 21.691 4.008-.724.046-10.207c.009-1.071 1.548-2.715 4.12-2.662 2.458.025 3.007 1.638 3.001 1.842l.071 11.817 3.952-.698.015-12.531c.026-1.206-2.641-3.753-6.928-3.771-2.038.004-3.166.465-3.74.805-.983.759-2.105 1.486-3.209 2.414 1.02-1.31 1.877-2.216 2.713-2.89l-.01-6.042z" />
</g>
<g clip-path="url(#_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi)"><text transform="matrix(1,0,0,1,87.023,43.899)"
style="font-family:'Open Sans';font-weight:700;font-size:11px;font-style:normal;fill:#ffffff;stroke:none;">user
repository</text></g>
<g fill-rule="evenodd" fill="#1793D1">
<path
d="M114.673 9.441l1.835-.459.087 21.868-1.865.318-.057-21.727zM119.663 15.968l1.608-.716.014 15.68-1.554.321-.068-15.285zm-.383-5.654l1.297-1.059 1.078 1.199-1.298 1.086-1.077-1.226zM124.296 15.682l1.835-.374.009 3.278c0 .141 1.008-3.662 5.868-3.577 4.719.026 5.491 3.679 5.465 4.498l.058 11.527-1.609.35-.009-11.386c.019-.333-.735-3.145-4.07-3.155-3.334-.01-5.644 2.422-5.64 3.975l.026 10.03-1.864.487-.069-15.653zM153.547 31.117l-1.836.375-.008-3.278c0-.141-1.009 3.662-5.868 3.577-4.719-.026-5.491-3.679-5.465-4.498l-.059-11.528 1.95-.393.031 11.386c0 .311.373 3.189 3.707 3.199 3.335.01 5.67-2.008 5.687-5.046l-.024-8.983 1.815-.464.07 15.653zM157.144 15.553l-1.287 1.007 4.935 6.458-5.263 7.46 1.365 1.014 4.947-6.929 5.107 7.093 1.263-1.007-5.473-7.584 4.366-6.132-1.342-1.136-3.977 5.675-4.641-5.919z" />
</g>
<path
d="M33.112 3.879c-2.147 5.264-3.442 8.708-5.833 13.816 1.466 1.553 3.265 3.363 6.187 5.406-3.141-1.292-5.284-2.59-6.885-3.937C23.521 25.549 18.728 34.643 9 52.121c7.645-4.414 13.572-7.135 19.095-8.173a13.965 13.965 0 01-.362-3.275l.009-.245c.121-4.898 2.669-8.665 5.688-8.409 3.018.255 5.364 4.436 5.243 9.334a13.819 13.819 0 01-.309 2.631c5.464 1.069 11.327 3.783 18.869 8.137-1.487-2.738-2.814-5.206-4.082-7.556-1.997-1.548-4.079-3.562-8.328-5.743 2.92.759 5.011 1.634 6.641 2.613C38.575 17.439 37.532 14.251 33.112 3.879z"
fill-rule="evenodd" fill="#1793D1" />
<g fill="#1793D1">
<path
d="M170.614 30.156v-1.354h-.505v-.181h1.216v.181h-.508v1.354h-.203zM171.536 30.156v-1.535h.306l.363 1.087c.033.101.058.176.073.227a8.63 8.63 0 01.082-.246l.367-1.068H173v1.535h-.196v-1.285l-.446 1.285h-.183l-.443-1.307v1.307h-.196z" />
</g>
<g fill="#1793D1">
<path
d="M57.471 47.815v-1.322h-.494v-.177h1.189v.177h-.496v1.322h-.199zM58.372 47.815v-1.499h.299l.355 1.061.072.222c.017-.054.043-.134.079-.24l.359-1.043h.267v1.499h-.191V46.56l-.436 1.255h-.179l-.433-1.276v1.276h-.192" />
</g>
<g clip-path="url(#prefix__b)"><text transform="translate(95.023 43.899)" font-family="sans-serif"
font-weight="700" font-size="10" fill="#fff">user repository</text></g>
<defs>
<clipPath id="_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi">
<rect x="0" y="0" width="86" height="14.98" transform="matrix(1,0,0,1,87,32.142)" />
<clipPath id="prefix__b">
<path transform="translate(87 32.142)" d="M0 0h86v14.98H0z" />
</clipPath>
</defs>
</g>

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

21409
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "antares",
"productName": "Antares",
"version": "0.7.16-beta.0",
"version": "0.7.20-beta.1",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT",
"repository": "https://github.com/antares-sql/antares.git",
@@ -27,7 +27,7 @@
"contributors:add": "all-contributors add",
"contributors:generate": "all-contributors generate"
},
"author": "Fabio Di Stasio <fabio286@gmail.com>",
"author": "Fabio Di Stasio <info@fabiodistasio.it>",
"main": "./dist/main.js",
"antares": {
"devtoolsId": "nhdogjmejiglipccpnnnanhbledajbpd"
@@ -67,7 +67,6 @@
"target": "deb",
"arch": [
"x64",
"arm64",
"armv7l"
]
},
@@ -75,7 +74,6 @@
"target": "AppImage",
"arch": [
"x64",
"arm64",
"armv7l"
]
}
@@ -121,13 +119,15 @@
},
"dependencies": {
"@electron/remote": "~2.0.1",
"@fabio286/ssh2-promise": "~1.0.4-b",
"@faker-js/faker": "~6.1.2",
"@mdi/font": "~7.1.96",
"@jamescoyle/vue-icon": "~0.1.2",
"@mdi/js": "~7.2.96",
"@turf/helpers": "~6.5.0",
"@vueuse/core": "~8.7.5",
"ace-builds": "~1.14.0",
"better-sqlite3": "~8.0.0",
"electron-log": "~4.4.1",
"@vueuse/core": "~10.4.1",
"ace-builds": "~1.24.1",
"better-sqlite3": "~9.1.1",
"electron-log": "~5.0.1",
"electron-store": "~8.1.0",
"electron-updater": "~4.6.5",
"electron-window-state": "~5.0.3",
@@ -143,13 +143,12 @@
"pg-connection-string": "~2.5.0",
"pg-query-stream": "~4.2.3",
"pgsql-ast-parser": "~7.2.1",
"pinia": "~2.0.28",
"pinia": "~2.1.6",
"source-map-support": "~0.5.20",
"spectre.css": "~0.5.9",
"sql-formatter": "~12.2.0",
"ssh2-promise": "~1.0.2",
"sql-formatter": "~13.0.0",
"v-mask": "~2.3.0",
"vue": "~3.2.45",
"vue": "~3.3.4",
"vue-i18n": "~9.2.2",
"vuedraggable": "~4.1.0"
},
@@ -172,13 +171,14 @@
"chalk": "~4.1.2",
"cross-env": "~7.0.2",
"css-loader": "~6.5.0",
"electron": "~22.0.3",
"electron-builder": "~22.10.3",
"electron": "~22.3.27",
"electron-builder": "~24.6.4",
"eslint": "~7.32.0",
"eslint-config-standard": "~16.0.3",
"eslint-plugin-import": "~2.24.2",
"eslint-plugin-node": "~11.1.0",
"eslint-plugin-promise": "~5.2.0",
"eslint-plugin-simple-import-sort": "~10.0.0",
"eslint-plugin-vue": "~8.0.3",
"file-loader": "~6.2.0",
"html-webpack-plugin": "~5.5.0",
@@ -193,10 +193,10 @@
"sass-loader": "~12.3.0",
"standard-version": "~9.3.1",
"style-loader": "~3.3.1",
"stylelint": "~14.9.1",
"stylelint-config-recommended-vue": "~1.4.0",
"stylelint-config-standard": "~26.0.0",
"stylelint-scss": "~4.3.0",
"stylelint": "^15.11.0",
"stylelint-config-recommended-vue": "~1.5.0",
"stylelint-config-standard": "~34.0.0",
"stylelint-scss": "~5.3.0",
"tree-kill": "~1.2.2",
"ts-loader": "~9.2.8",
"ts-node": "~10.9.1",
@@ -208,10 +208,5 @@
"webpack-cli": "~4.9.1",
"webpack-dev-server": "~4.11.1",
"xvfb-maybe": "~0.2.1"
},
"overrides": {
"ssh2-promise": {
"ssh2": "github:Fabio286/ssh2"
}
}
}

View File

@@ -63,43 +63,39 @@ async function restartElectron () {
if (!manualRestart) process.exit(0);
});
}
function startWorkers () {
const compiler = webpack(workersConfig);
const { name } = compiler;
function startMain () {
const webpackSetup = webpack([mainConfig, workersConfig]);
webpackSetup.compilers.forEach((compiler) => {
const { name } = compiler;
switch (name) {
case 'workers':
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(chalk.gray(`\nCompiled ${name} script!`));
console.log(
chalk.gray(`\nWatching file changes for ${name} script...`)
);
});
break;
case 'main':
default:
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(chalk.gray(`\nCompiled ${name} script!`));
manualRestart = true;
await restartElectron();
setTimeout(() => {
manualRestart = false;
}, 2500);
console.log(
chalk.gray(`\nWatching file changes for ${name} script...`)
);
});
break;
}
compiler.hooks.afterEmit.tap('afterEmit', () => {
console.log(chalk.gray(`\nCompiled ${name} script!`));
console.log(chalk.gray(`\nWatching file changes for ${name} script...`));
});
webpackSetup.watch({ aggregateTimeout: 500 }, err => {
compiler.watch({ aggregateTimeout: 500 }, err => {
if (err) console.error(chalk.red(err));
});
}
function startMain () {
const compiler = webpack(mainConfig);
const { name } = compiler;
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(chalk.gray(`\nCompiled ${name} script!`));
manualRestart = true;
await restartElectron();
startWorkers();
setTimeout(() => {
manualRestart = false;
}, 2500);
console.log(chalk.gray(`\nWatching file changes for ${name} script...`));
});
compiler.watch({ aggregateTimeout: 500 }, err => {
if (err) console.error(chalk.red(err));
});
}

View File

@@ -42,6 +42,7 @@ const downloadFile = url => {
await unzip(filePath, destFolder);
fs.unlinkSync(filePath);
fs.unlinkSync(`${destFolder}/package.json`);// <- Avoid to display annoyng npm script in vscode
process.exit();
}
catch (error) {
console.log(error);

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { localesNames } from '../src/renderer/i18n/supported-locales';
import { enUS } from '../src/renderer/i18n/en-US';
import { localesNames } from '../src/renderer/i18n/supported-locales';
const locale = process.argv[2];
let fullCount = 0;
let checkCount = 0;

View File

@@ -31,10 +31,10 @@ parts:
fi
# Get the latest releases json
echo "Get GitHub releases..."
wget --quiet https://api.github.com/repos/fabio286/antares/releases/latest -O releases.json
wget --quiet https://api.github.com/repos/fabio286/antares/releases -O releases.json
# Get the version from the tag_name and the download URL.
VERSION=$(jq . releases.json | grep tag_name | cut -d'"' -f4 | sed s'/release-//')
DEB_URL=$(cat releases.json | jq -r ".assets[] | select(.name | test(\"${FILTER}\")) | .browser_download_url")
VERSION=$(jq . releases.json | grep tag_name | head -1 | cut -d'"' -f4 | sed s'/release-//')
DEB_URL=$(cat releases.json | jq -r ".[0].assets[] | select(.name | test(\"${FILTER}\")) | .browser_download_url")
DEB=$(basename "${DEB_URL}")
echo "Downloading ${DEB_URL}..."
wget --quiet "${DEB_URL}" -O "${SNAPCRAFT_PART_INSTALL}/${DEB}"
@@ -79,7 +79,7 @@ parts:
- libdb5.3
- libdbus-1-3
- libexpat1
- libffi7
- libffi8
- libgcc-s1
- libgcrypt20
- libglib2.0-0
@@ -87,9 +87,9 @@ parts:
- libgnutls30
- libgpg-error0
- libgssapi-krb5-2
- libhogweed5
- libhogweed6
- libidn2-0
- libjson-c4
- libjson-c5
- libk5crypto3
- libkeyutils1
- libkrb5-3
@@ -97,12 +97,12 @@ parts:
- liblz4-1
- liblzma5
- libmount1
- libnettle7
- libnettle8
- libp11-kit0
- libpcre2-8-0
- libselinux1
- libsqlite3-0
- libssl1.1
- libssl3
- libstdc++6
- libsystemd0
- libtasn1-6

View File

@@ -51,6 +51,7 @@ export default class {
{ name: 'collation', group: 'database', types: ['string'] },
{ name: 'engine', group: 'database', types: ['string'] },
{ name: 'now', group: 'date', types: ['string', 'datetime'] },
{ name: 'past', group: 'date', types: ['string', 'datetime'] },
{ name: 'future', group: 'date', types: ['string', 'datetime'] },
// { name: 'between', group: 'date', types: ['string'] },
@@ -161,7 +162,9 @@ export default class {
{ name: 'filePath', group: 'system', types: ['string'] },
{ name: 'semver', group: 'system', types: ['string'] },
{ name: 'now', group: 'time', types: ['string', 'time'] },
{ name: 'recent', group: 'time', types: ['string', 'time'] },
{ name: 'random', group: 'time', types: ['string', 'time'] },
{ name: 'vehicle', group: 'vehicle', types: ['string'] },
{ name: 'manufacturer', group: 'vehicle', types: ['string'] },

View File

@@ -9,6 +9,7 @@ export const defaults: Customizations = {
dataTypes: [],
indexTypes: [],
foreignActions: [],
operators: ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'],
// Core
database: false,
collations: false,

View File

@@ -1,6 +1,6 @@
import firebirdTypes from '../data-types/firebird';
import { Customizations } from '../interfaces/customizations';
import { defaults } from './defaults';
import firebirdTypes from '../data-types/firebird';
export const customizations: Customizations = {
...defaults,

View File

@@ -1,7 +1,7 @@
import * as firebird from 'common/customizations/firebird';
import * as mysql from 'common/customizations/mysql';
import * as postgresql from 'common/customizations/postgresql';
import * as sqlite from 'common/customizations/sqlite';
import * as firebird from 'common/customizations/firebird';
import { Customizations } from 'common/interfaces/customizations';
export default {

View File

@@ -1,6 +1,6 @@
import mysqlTypes from '../data-types/mysql';
import { Customizations } from '../interfaces/customizations';
import { defaults } from './defaults';
import mysqlTypes from '../data-types/mysql';
export const customizations: Customizations = {
...defaults,
@@ -9,6 +9,7 @@ export const customizations: Customizations = {
defaultUser: 'root',
defaultDatabase: null,
dataTypes: mysqlTypes,
operators: ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'RLIKE', 'NOT RLIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'],
indexTypes: [
'PRIMARY',
'INDEX',

View File

@@ -1,6 +1,6 @@
import postgresqlTypes from '../data-types/postgresql';
import { Customizations } from '../interfaces/customizations';
import { defaults } from './defaults';
import postgresqlTypes from '../data-types/postgresql';
export const customizations: Customizations = {
...defaults,

View File

@@ -1,6 +1,6 @@
import sqliteTypes from '../data-types/sqlite';
import { Customizations } from '../interfaces/customizations';
import { defaults } from './defaults';
import sqliteTypes from '../data-types/sqlite';
export const customizations: Customizations = {
...defaults,

View File

@@ -1,14 +1,15 @@
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import * as mysql from 'mysql2/promise';
import * as pg from 'pg';
import { FirebirdSQLClient } from 'src/main/libs/clients/FirebirdSQLClient';
import MysqlExporter from 'src/main/libs/exporters/sql/MysqlExporter';
import PostgreSQLExporter from 'src/main/libs/exporters/sql/PostgreSQLExporter';
import MySQLImporter from 'src/main/libs/importers/sql/MySQLlImporter';
import PostgreSQLImporter from 'src/main/libs/importers/sql/PostgreSQLImporter';
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { MySQLClient } from '../../main/libs/clients/MySQLClient';
import { PostgreSQLClient } from '../../main/libs/clients/PostgreSQLClient';
import { SQLiteClient } from '../../main/libs/clients/SQLiteClient';
import { FirebirdSQLClient } from 'src/main/libs/clients/FirebirdSQLClient';
export type Client = MySQLClient | PostgreSQLClient | SQLiteClient | FirebirdSQLClient
export type ClientCode = 'mysql' | 'maria' | 'pg' | 'sqlite' | 'firebird'
@@ -64,6 +65,7 @@ export interface ConnectionParams {
sshKey?: string;
sshPort?: number;
sshPassphrase?: string;
sshKeepAliveInterval?: number;
}
export interface TypeInformations {
@@ -361,8 +363,7 @@ export interface QueryBuilderObject {
offset: number;
join: string[];
update: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
insert: {[key: string]: any}[];
insert: {[key: string]: string | boolean | number }[];
delete: boolean;
}

View File

@@ -1,4 +1,5 @@
import { TypesGroup } from './antares';
import { TableFilterOperator } from './tableApis';
export interface Customizations {
// Defaults
@@ -8,6 +9,7 @@ export interface Customizations {
dataTypes?: TypesGroup[];
indexTypes?: string[];
foreignActions?: string[];
operators?: TableFilterOperator[];
// Core
database?: boolean;
collations?: boolean;

View File

@@ -21,10 +21,12 @@ export interface TableDeleteParams {
rows: {[key: string]: any};
}
export type TableFilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'NOT LIKE' | 'RLIKE' | 'NOT RLIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL'
export interface TableFilterClausole {
active: boolean;
field: string;
op: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL';
op:TableFilterOperator;
value: '';
value2: '';
}

View File

@@ -0,0 +1,28 @@
import * as crypto from 'crypto';
const algorithm = 'aes-256-gcm';
function encrypt (text: string, password: string) {
const iv = crypto.randomBytes(16);
const key = crypto.scryptSync(password, 'antares', 32);
const cipher = crypto.createCipheriv(algorithm, key, iv);
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
authTag: authTag.toString('hex'),
content: encrypted.toString('hex')
};
}
function decrypt (hash: { iv: string; content: string; authTag: string }, password: string) {
const key = crypto.scryptSync(password, 'antares', 32);
const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(hash.iv, 'hex'));
decipher.setAuthTag(Buffer.from(hash.authTag, 'hex'));
const decrpyted = decipher.update(hash.content, 'hex', 'utf8') + decipher.final('utf8');
return decrpyted;
}
export { decrypt, encrypt };

View File

@@ -0,0 +1,17 @@
import { faker } from '@faker-js/faker';
import * as moment from 'moment';
export const fakerCustom = {
seed: faker.seed,
setLocale: faker.setLocale,
...faker,
date: {
now: () => moment().format('YYYY-MM-DD HH:mm:ss'),
...faker.date
},
time: {
now: () => moment().format('HH:mm:ss'),
random: () => moment(faker.date.recent()).format('HH:mm:ss'),
...faker.time
}
};

View File

@@ -1,12 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-useless-escape */
import * as moment from 'moment';
import { lineString, point, polygon } from '@turf/helpers';
import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_MULTI_SPATIAL, NUMBER, SPATIAL, TEXT_SEARCH } from 'common/fieldTypes';
import * as moment from 'moment';
import customizations from '../customizations';
import { ClientCode } from '../interfaces/antares';
import { BLOB, BIT, DATE, DATETIME, FLOAT, SPATIAL, IS_MULTI_SPATIAL, NUMBER, TEXT_SEARCH } from 'common/fieldTypes';
import hexToBinary, { HexChar } from './hexToBinary';
import { getArrayDepth } from './getArrayDepth';
import hexToBinary, { HexChar } from './hexToBinary';
/**
* Escapes a string fo SQL use

View File

@@ -135,4 +135,4 @@ for (let i = 1; i <= 9; i++) {
});
}
export { shortcuts, ShortcutRecord };
export { ShortcutRecord, shortcuts };

View File

@@ -1,35 +1,76 @@
import { app, ipcMain, dialog } from 'electron';
import { app, dialog, ipcMain, safeStorage } from 'electron';
import * as Store from 'electron-store';
import { validateSender } from '../libs/misc/validateSender';
import { ShortcutRegister } from '../libs/ShortcutRegister';
export default () => {
ipcMain.on('close-app', () => {
ipcMain.on('close-app', (event) => {
if (!validateSender(event.senderFrame)) {
return {
status: 'error',
response: 'Unauthorized process'
};
}
app.exit();
});
ipcMain.on('set-key', (event, key) => {
if (safeStorage.isEncryptionAvailable()) {
const sessionStore = new Store({
name: 'session',
fileExtension: ''
});
const encrypted = safeStorage.encryptString(key);
sessionStore.set('key', encrypted);
event.returnValue = true;
}
});
ipcMain.on('get-key', (event) => {
if (!safeStorage.isEncryptionAvailable()) {
event.returnValue = false;
return;
}
const sessionStore = new Store({
name: 'session',
fileExtension: ''
});
const encrypted = sessionStore.get('key') as string;
const key = safeStorage.decryptString(Buffer.from(encrypted, 'utf-8'));
event.returnValue = key;
});
ipcMain.handle('show-open-dialog', (event, options) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
return dialog.showOpenDialog(options);
});
ipcMain.handle('get-download-dir-path', () => {
ipcMain.handle('get-download-dir-path', (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
return app.getPath('downloads');
});
ipcMain.handle('resotre-default-shortcuts', () => {
ipcMain.handle('resotre-default-shortcuts', (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const shortCutRegister = ShortcutRegister.getInstance();
shortCutRegister.restoreDefaults();
});
ipcMain.handle('reload-shortcuts', () => {
ipcMain.handle('reload-shortcuts', (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const shortCutRegister = ShortcutRegister.getInstance();
shortCutRegister.reload();
});
ipcMain.handle('update-shortcuts', (event, shortcuts) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const shortCutRegister = ShortcutRegister.getInstance();
shortCutRegister.updateShortcuts(shortcuts);
});
ipcMain.handle('unregister-shortcuts', () => {
ipcMain.handle('unregister-shortcuts', (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const shortCutRegister = ShortcutRegister.getInstance();
shortCutRegister.unregister();
});

View File

@@ -1,11 +1,15 @@
import * as antares from 'common/interfaces/antares';
import * as fs from 'fs';
import { ipcMain } from 'electron';
import { ClientsFactory } from '../libs/ClientsFactory';
import * as fs from 'fs';
import { SslOptions } from 'mysql2';
import { ClientsFactory } from '../libs/ClientsFactory';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('test-connection', async (event, conn: antares.ConnectionParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const params = {
host: conn.host,
port: +conn.port,
@@ -23,6 +27,7 @@ export default (connections: {[key: string]: antares.Client}) => {
port: number;
privateKey: string;
passphrase: string;
keepaliveInterval: number;
}
};
@@ -49,7 +54,8 @@ export default (connections: {[key: string]: antares.Client}) => {
password: conn.sshPass,
port: conn.sshPort ? conn.sshPort : 22,
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
passphrase: conn.sshPassphrase
passphrase: conn.sshPassphrase,
keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : null
};
}
@@ -80,6 +86,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('connect', async (event, conn: antares.ConnectionParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const params = {
host: conn.host,
port: +conn.port,
@@ -98,6 +106,7 @@ export default (connections: {[key: string]: antares.Client}) => {
port: number;
privateKey: string;
passphrase: string;
keepaliveInterval: number;
}
};
@@ -127,7 +136,8 @@ export default (connections: {[key: string]: antares.Client}) => {
password: conn.sshPass,
port: conn.sshPort ? conn.sshPort : 22,
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
passphrase: conn.sshPassphrase
passphrase: conn.sshPassphrase,
keepaliveInterval: conn.sshKeepAliveInterval ? conn.sshKeepAliveInterval*1000 : null
};
}
@@ -153,6 +163,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('disconnect', (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
connections[uid].destroy();
delete connections[uid];
});

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-databases', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getDatabases();
return { status: 'success', response: result };

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-function-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getFunctionInformations(params);
return { status: 'success', response: result };
@@ -13,6 +17,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-function', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropFunction(params);
return { status: 'success' };
@@ -23,6 +29,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-function', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterFunction(params);
return { status: 'success' };
@@ -33,6 +41,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-trigger-function', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterTriggerFunction(params);
return { status: 'success' };
@@ -43,6 +53,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-function', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createFunction(params);
return { status: 'success' };
@@ -53,6 +65,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-trigger-function', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createTriggerFunction(params);
return { status: 'success' };

View File

@@ -1,17 +1,17 @@
import * as antares from 'common/interfaces/antares';
import connection from './connection';
import tables from './tables';
import views from './views';
import triggers from './triggers';
import routines from './routines';
import functions from './functions';
import schedulers from './schedulers';
import updates from './updates';
import application from './application';
import connection from './connection';
import database from './database';
import functions from './functions';
import routines from './routines';
import schedulers from './schedulers';
import schema from './schema';
import tables from './tables';
import triggers from './triggers';
import updates from './updates';
import users from './users';
import views from './views';
const connections: {[key: string]: antares.Client} = {};

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-routine-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getRoutineInformations(params);
return { status: 'success', response: result };
@@ -13,6 +17,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-routine', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropRoutine(params);
return { status: 'success' };
@@ -23,6 +29,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-routine', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterRoutine(params);
return { status: 'success' };
@@ -33,6 +41,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-routine', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createRoutine(params);
return { status: 'success' };

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-scheduler-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getEventInformations(params);
return { status: 'success', response: result };
@@ -13,6 +17,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-scheduler', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropEvent(params);
return { status: 'success' };
@@ -23,6 +29,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-scheduler', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterEvent(params);
return { status: 'success' };
@@ -33,6 +41,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-scheduler', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createEvent(params);
return { status: 'success' };
@@ -43,6 +53,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('toggle-scheduler', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
if (!params.enabled)
await connections[params.uid].enableEvent({ ...params });

View File

@@ -1,17 +1,18 @@
import * as antares from 'common/interfaces/antares';
import * as workers from 'common/interfaces/workers';
import { dialog, ipcMain } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
import { ChildProcess, fork } from 'child_process';
import { ipcMain, dialog } from 'electron';
import { Worker } from 'worker_threads';
const isDevelopment = process.env.NODE_ENV !== 'production';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
let exporter: ChildProcess = null;
let importer: ChildProcess = null;
let exporter: Worker = null;
let importer: Worker = null;
ipcMain.handle('create-schema', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createSchema(params);
@@ -23,6 +24,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('update-schema', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterSchema(params);
@@ -34,6 +37,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('delete-schema', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropSchema(params);
@@ -45,6 +50,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-schema-collation', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const collation = await connections[params.uid].getDatabaseCollation(
params
@@ -61,6 +68,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-structure', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const structure: unknown = await connections[params.uid].getStructure(
params.schemas
@@ -74,6 +83,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-collations', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getCollations();
@@ -85,6 +96,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-variables', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getVariables();
@@ -96,6 +109,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-engines', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result: unknown = await connections[uid].getEngines();
@@ -107,6 +122,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-version', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getVersion();
@@ -118,6 +135,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-processes', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getProcesses();
@@ -129,6 +148,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('kill-process', async (event, { uid, pid }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].killProcess(pid);
@@ -140,6 +161,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!schema) return;
try {
@@ -152,6 +175,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('raw-query', async (event, { uid, query, schema, tabUid, autocommit }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!query) return;
try {
@@ -171,8 +196,10 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('export', (event, { uid, type, tables, ...rest }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (exporter !== null) {
exporter.kill();
exporter.terminate();
return;
}
@@ -197,11 +224,12 @@ export default (connections: {[key: string]: antares.Client}) => {
}
}
// Init exporter process
exporter = fork(isDevelopment ? './dist/exporter.js' : path.resolve(__dirname, './exporter.js'), [], {
execArgv: isDevelopment ? ['--inspect=9224'] : undefined
});
exporter.send({
// Init exporter thread
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
exporter = new Worker(new URL('../workers/exporter', import.meta.url));
exporter.postMessage({
type: 'init',
client: {
name: type,
@@ -212,32 +240,43 @@ export default (connections: {[key: string]: antares.Client}) => {
});
// Exporter message listener
exporter.on('message', ({ type, payload }: workers.WorkerIpcMessage) => {
exporter.stdout.on('data', (buff: Buffer) => {
let message;
try { // Ignore non-JSON data (console.log output)
message = JSON.parse(buff.toString());
}
catch (_) {
if (process.env.NODE_ENV === 'development') console.log('EXPORTER:', buff.toString());
return;
}
const { type, payload } = message as workers.WorkerIpcMessage;
switch (type) {
case 'export-progress':
event.sender.send('export-progress', payload);
break;
case 'end':
setTimeout(() => { // Ensures that writing process has finished
exporter.kill();
setTimeout(() => { // Ensures that writing thread has finished
exporter?.terminate();
exporter = null;
}, 2000);
resolve({ status: 'success', response: payload });
break;
case 'cancel':
exporter.kill();
exporter?.terminate();
exporter = null;
resolve({ status: 'error', response: 'Operation cancelled' });
break;
case 'error':
exporter.kill();
exporter?.terminate();
exporter = null;
resolve({ status: 'error', response: payload });
break;
}
});
exporter.on('exit', code => {
exporter.on('close', code => {
exporter = null;
resolve({ status: 'error', response: `Operation ended with code: ${code}` });
});
@@ -245,7 +284,9 @@ export default (connections: {[key: string]: antares.Client}) => {
});
});
ipcMain.handle('abort-export', async () => {
ipcMain.handle('abort-export', async (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
let willAbort = false;
if (exporter) {
@@ -259,7 +300,7 @@ export default (connections: {[key: string]: antares.Client}) => {
if (result.response === 1) {
willAbort = true;
exporter.send({ type: 'cancel' });
exporter.postMessage({ type: 'cancel' });
}
}
@@ -267,8 +308,10 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('import-sql', async (event, options) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (importer !== null) {
importer.kill();
importer.terminate();
return;
}
@@ -276,18 +319,30 @@ export default (connections: {[key: string]: antares.Client}) => {
(async () => {
const dbConfig = await connections[options.uid].getDbConfig();
// Init importer process
importer = fork(isDevelopment ? './dist/importer.js' : path.resolve(__dirname, './importer.js'), [], {
execArgv: isDevelopment ? ['--inspect=9224'] : undefined
});
importer.send({
// Init importer thread
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
importer = new Worker(new URL('../workers/importer', import.meta.url));
importer.postMessage({
type: 'init',
dbConfig,
options
});
// Importer message listener
importer.on('message', ({ type, payload }: workers.WorkerIpcMessage) => {
importer.stdout.on('data', (buff: Buffer) => {
let message;
try { // Ignore non-JSON data (console.log output)
message = JSON.parse(buff.toString());
}
catch (_) {
if (process.env.NODE_ENV === 'development') console.log('IMPORTER:', buff.toString());
return;
}
const { type, payload } = message as workers.WorkerIpcMessage;
switch (type) {
case 'import-progress':
event.sender.send('import-progress', payload);
@@ -297,28 +352,35 @@ export default (connections: {[key: string]: antares.Client}) => {
break;
case 'end':
setTimeout(() => { // Ensures that writing process has finished
importer?.kill();
importer?.terminate();
importer = null;
}, 2000);
resolve({ status: 'success', response: payload });
break;
case 'cancel':
importer.kill();
importer.terminate();
importer = null;
resolve({ status: 'error', response: 'Operation cancelled' });
break;
case 'error':
importer.kill();
importer.terminate();
importer = null;
resolve({ status: 'error', response: payload });
break;
}
});
importer.on('close', code => {
importer = null;
resolve({ status: 'error', response: `Operation ended with code: ${code}` });
});
})();
});
});
ipcMain.handle('abort-import-sql', async () => {
ipcMain.handle('abort-import-sql', async (event) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
let willAbort = false;
if (importer) {
@@ -332,7 +394,7 @@ export default (connections: {[key: string]: antares.Client}) => {
if (result.response === 1) {
willAbort = true;
importer.send({ type: 'cancel' });
importer.postMessage({ type: 'cancel' });
}
}
@@ -340,6 +402,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('kill-tab-query', async (event, { uid, tabUid }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!tabUid) return;
try {
@@ -352,6 +416,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('commit-tab', async (event, { uid, tabUid }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!tabUid) return;
try {
@@ -364,6 +430,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('rollback-tab', async (event, { uid, tabUid }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!tabUid) return;
try {
@@ -376,6 +444,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('destroy-connection-to-commit', async (event, { uid, tabUid }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (!tabUid) return;
try {

View File

@@ -1,15 +1,19 @@
import * as fs from 'fs';
import customizations from 'common/customizations';
import { ARRAY, BIT, BLOB, BOOLEAN, DATE, DATETIME, FLOAT, LONG_TEXT, NUMBER, TEXT, TEXT_SEARCH } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
import { InsertRowsParams } from 'common/interfaces/tableApis';
import { ipcMain } from 'electron';
import { faker } from '@faker-js/faker';
import * as moment from 'moment';
import { fakerCustom } from 'common/libs/fakerCustom';
import { sqlEscaper } from 'common/libs/sqlUtils';
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME, BOOLEAN } from 'common/fieldTypes';
import customizations from 'common/customizations';
import { ipcMain } from 'electron';
import * as fs from 'fs';
import * as moment from 'moment';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-table-columns', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTableColumns(params);
return { status: 'success', response: result };
@@ -20,6 +24,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams, where }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const offset = (page - 1) * limit;
const query = connections[uid]
@@ -45,6 +51,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-table-count', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTableApproximateCount(params);
return { status: 'success', response: result };
@@ -55,6 +63,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-table-options', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTableOptions(params);
return { status: 'success', response: result };
@@ -65,6 +75,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-table-indexes', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTableIndexes(params);
@@ -76,6 +88,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-table-ddl', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTableDll(params);
@@ -87,6 +101,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-key-usage', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getKeyUsage(params);
@@ -98,6 +114,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('update-table-cell', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
delete params.row._antares_id;
const { stringsWrapper: sw } = customizations[connections[params.uid]._client];
@@ -227,6 +245,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('delete-table-rows', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
if (params.primary) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const idString = params.rows.map((row: {[key: string]: any}) => {
@@ -281,6 +301,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('insert-table-fake-rows', async (event, params: InsertRowsParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try { // TODO: move to client classes
const rows: {[key: string]: string | number | boolean | Date | Buffer}[] = [];
@@ -349,19 +371,19 @@ export default (connections: {[key: string]: antares.Client}) => {
let fakeValue;
if (params.locale)
faker.locale = params.locale;
fakerCustom.locale = params.locale;
if (Object.keys(params.row[key].params).length) {
Object.keys(params.row[key].params).forEach(param => {
if (!isNaN(params.row[key].params[param]))
parsedParams[param] = +params.row[key].params[param];
if (!isNaN(params.row[key].params[param]))// Converts string numerics params to number
parsedParams[param] = Number(params.row[key].params[param]);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fakeValue = (faker as any)[params.row[key].group][params.row[key].method](parsedParams);
fakeValue = (fakerCustom as any)[params.row[key].group][params.row[key].method](parsedParams);
}
else
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fakeValue = (faker as any)[params.row[key].group][params.row[key].method]();
fakeValue = (fakerCustom as any)[params.row[key].group][params.row[key].method]();
if (typeof fakeValue === 'string') {
if (params.row[key].length)
@@ -403,6 +425,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const query = connections[uid]
.select(`${column} AS foreign_column`)
@@ -436,6 +460,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-table', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createTable(params);
return { status: 'success' };
@@ -446,6 +472,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-table', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterTable(params);
return { status: 'success' };
@@ -456,6 +484,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('duplicate-table', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].duplicateTable(params);
return { status: 'success' };
@@ -466,6 +496,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('truncate-table', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].truncateTable(params);
return { status: 'success' };
@@ -476,6 +508,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-table', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropTable(params);
return { status: 'success' };

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-trigger-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getTriggerInformations(params);
return { status: 'success', response: result };
@@ -13,6 +17,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-trigger', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropTrigger(params);
return { status: 'success' };
@@ -23,6 +29,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-trigger', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterTrigger(params);
return { status: 'success' };
@@ -33,6 +41,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-trigger', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createTrigger(params);
return { status: 'success' };
@@ -43,6 +53,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('toggle-trigger', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
if (!params.enabled)
await connections[params.uid].enableTrigger(params);

View File

@@ -1,6 +1,7 @@
import { ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import * as log from 'electron-log/main';
import * as Store from 'electron-store';
import { autoUpdater } from 'electron-updater';
const persistentStore = new Store({
name: 'settings',
@@ -51,15 +52,15 @@ export default () => {
mainWindow.reply('update-not-available');
});
autoUpdater.on('download-progress', data => {
mainWindow.reply('download-progress', data);
autoUpdater.on('download-progress', event => {
mainWindow.reply('download-progress', event);
});
autoUpdater.on('update-downloaded', () => {
mainWindow.reply('update-downloaded');
});
// autoUpdater.logger = require('electron-log');
// autoUpdater.logger.transports.console.format = '{h}:{i}:{s} {text}';
// autoUpdater.logger.transports.file.level = 'info';
log.transports.file.level = 'info';
// log.transports.console.format = '{h}:{i}:{s} {text}';
autoUpdater.logger = log;
};

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-users', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[uid].getUsers();
return { status: 'success', response: result };

View File

@@ -1,8 +1,12 @@
import * as antares from 'common/interfaces/antares';
import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-view-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
const result = await connections[params.uid].getViewInformations(params);
return { status: 'success', response: result };
@@ -13,6 +17,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('drop-view', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].dropView(params);
return { status: 'success' };
@@ -23,6 +29,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('alter-view', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].alterView(params);
return { status: 'success' };
@@ -33,6 +41,8 @@ export default (connections: {[key: string]: antares.Client}) => {
});
ipcMain.handle('create-view', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try {
await connections[params.uid].createView(params);
return { status: 'success' };

View File

@@ -1,8 +1,9 @@
import * as antares from 'common/interfaces/antares';
import { FirebirdSQLClient } from './clients/FirebirdSQLClient';
import { MySQLClient } from './clients/MySQLClient';
import { PostgreSQLClient } from './clients/PostgreSQLClient';
import { SQLiteClient } from './clients/SQLiteClient';
import { FirebirdSQLClient } from './clients/FirebirdSQLClient';
export class ClientsFactory {
static getClient (args: antares.ClientParams) {

View File

@@ -1,6 +1,6 @@
import { ShortcutRecord, shortcuts } from 'common/shortcuts';
import { BrowserWindow, globalShortcut, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
import * as Store from 'electron-store';
import { ShortcutRecord, shortcuts } from 'common/shortcuts';
const shortcutsStore = new Store({ name: 'shortcuts' });
const isDevelopment = process.env.NODE_ENV !== 'production';

View File

@@ -1,7 +1,7 @@
import * as antares from 'common/interfaces/antares';
import mysql from 'mysql2/promise';
import * as pg from 'pg';
import SSH2Promise from 'ssh2-promise';
import SSH2Promise = require('@fabio286/ssh2-promise');
const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
// Remove comments, newlines and multiple spaces
@@ -10,13 +10,13 @@ const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
}
if (process.env.NODE_ENV === 'development') console.log(escapedSql);
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);
};
/**
* As Simple As Possible Query Builder Core
*/
export abstract class AntaresCore {
export abstract class BaseClient {
_client: antares.ClientCode;
protected _cUid: string
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};

View File

@@ -1,11 +1,12 @@
import * as path from 'path';
import * as antares from 'common/interfaces/antares';
import * as firebird from 'node-firebird';
import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/firebird';
import { FLOAT, NUMBER } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
import * as firebird from 'node-firebird';
import * as path from 'path';
export class FirebirdSQLClient extends AntaresCore {
import { BaseClient } from './BaseClient';
export class FirebirdSQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, firebird.Transaction>;
@@ -117,6 +118,10 @@ export class FirebirdSQLClient extends AntaresCore {
return null;
}
getDatabases (): null[] {
return [];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async getStructure (_schemas: Set<string>) {
interface TableResult {
@@ -156,10 +161,10 @@ export class FirebirdSQLClient extends AntaresCore {
const { rows: tables } = await this.raw<antares.QueryResult<TableResult>>(`
SELECT
rdb$relation_name AS name,
rdb$format AS format,
rdb$description AS description,
'table' AS type
rdb$relation_name AS NAME,
rdb$format AS FORMAT,
rdb$description AS DESCRIPTION,
'table' AS TYPE
FROM RDB$RELATIONS a
WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0
AND RDB$RELATION_TYPE = 0
@@ -167,8 +172,8 @@ export class FirebirdSQLClient extends AntaresCore {
const { rows: views } = await this.raw<antares.QueryResult<TableResult>>(`
SELECT
DISTINCT RDB$VIEW_NAME AS name,
'view' AS type
DISTINCT RDB$VIEW_NAME AS NAME,
'view' AS TYPE
FROM RDB$VIEW_RELATIONS
`);
@@ -176,9 +181,9 @@ export class FirebirdSQLClient extends AntaresCore {
const { rows: triggers } = await this.raw<antares.QueryResult<TriggersResult>>(`
SELECT
RDB$TRIGGER_NAME as name,
RDB$RELATION_NAME as relation,
RDB$TRIGGER_SOURCE as source
RDB$TRIGGER_NAME as NAME,
RDB$RELATION_NAME as RELATION,
RDB$TRIGGER_SOURCE as SOURCE
FROM RDB$TRIGGERS
WHERE RDB$SYSTEM_FLAG=0
ORDER BY RDB$TRIGGER_NAME;
@@ -207,8 +212,8 @@ export class FirebirdSQLClient extends AntaresCore {
schemaSize += tableSize;
return {
name: table.NAME.trim(),
type: table.TYPE.trim(),
name: table.NAME?.trim(),
type: table.TYPE?.trim(),
rows: false,
size: false
};
@@ -217,8 +222,8 @@ export class FirebirdSQLClient extends AntaresCore {
// TRIGGERS
const remappedTriggers = triggersArr.map(trigger => {
return {
name: trigger.NAME.trim(),
table: trigger.RELATION.trim(),
name: trigger.NAME?.trim(),
table: trigger.RELATION?.trim(),
statement: trigger.SOURCE
};
});
@@ -226,7 +231,7 @@ export class FirebirdSQLClient extends AntaresCore {
// PROCEDURES
const remappedProcedures = proceduresArr.map(procedure => {
return {
name: procedure.NAME.trim(),
name: procedure.NAME?.trim(),
definer: procedure.DEFINER,
comment: procedure.COMMENT?.trim()
};

View File

@@ -1,11 +1,12 @@
import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/mysql';
import * as antares from 'common/interfaces/antares';
import * as mysql from 'mysql2/promise';
import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/mysql';
import SSH2Promise = require('ssh2-promise');
import SSHConfig from 'ssh2-promise/lib/sshConfig';
export class MySQLClient extends AntaresCore {
import { BaseClient } from './BaseClient';
export class MySQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, mysql.Connection | mysql.PoolConnection>;
@@ -155,7 +156,6 @@ export class MySQLClient extends AntaresCore {
this._ssh = new SSH2Promise({
...this._params.ssh,
keepaliveInterval: 30*60*1000,
debug: process.env.NODE_ENV !== 'production' ? (s) => console.log(s) : null
});
@@ -168,7 +168,10 @@ export class MySQLClient extends AntaresCore {
dbConfig.port = tunnel.localPort;
}
catch (err) {
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
throw err;
}
}
@@ -187,7 +190,10 @@ export class MySQLClient extends AntaresCore {
this._connection.end();
clearInterval(this._keepaliveTimer);
this._keepaliveTimer = undefined;
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
}
async getConnection () {
@@ -256,9 +262,12 @@ export class MySQLClient extends AntaresCore {
}
private async keepAlive () {
const connection = await (this._connection as mysql.Pool).getConnection();
await connection.ping();
connection.release();
try {
const connection = await (this._connection as mysql.Pool).getConnection();
await connection.ping();
connection.release();
}
catch (_) {}
}
use (schema: string) {

View File

@@ -1,12 +1,13 @@
import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/postgresql';
import * as antares from 'common/interfaces/antares';
import * as pg from 'pg';
import * as pgAst from 'pgsql-ast-parser';
import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/postgresql';
import SSH2Promise = require('ssh2-promise');
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { ConnectionOptions } from 'tls';
import { BaseClient } from './BaseClient';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function pgToString (value: any) {
return value.toString();
@@ -80,7 +81,7 @@ type builtinsTypes =
'JSONB' |
'REGNAMESPACE' |
'REGROLE';
export class PostgreSQLClient extends AntaresCore {
export class PostgreSQLClient extends BaseClient {
private _schema?: string;
private _runningConnections: Map<string, number>;
private _connectionsToCommit: Map<string, pg.Client | pg.PoolClient>;
@@ -167,7 +168,6 @@ export class PostgreSQLClient extends AntaresCore {
try {
this._ssh = new SSH2Promise({
...this._params.ssh,
keepaliveInterval: 30*60*1000,
debug: process.env.NODE_ENV !== 'production' ? (s) => console.log(s) : null
});
@@ -180,7 +180,10 @@ export class PostgreSQLClient extends AntaresCore {
dbConfig.port = tunnel.localPort;
}
catch (err) {
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
throw err;
}
}
@@ -236,13 +239,19 @@ export class PostgreSQLClient extends AntaresCore {
this._connection.end();
clearInterval(this._keepaliveTimer);
this._keepaliveTimer = undefined;
if (this._ssh) this._ssh.close();
if (this._ssh) {
this._ssh.close();
this._ssh.closeTunnel();
}
}
private async keepAlive () {
const connection = await this._connection.connect() as pg.PoolClient;
await connection.query('SELECT 1+1');
connection.release();
try {
const connection = await this._connection.connect() as pg.PoolClient;
await connection.query('SELECT 1+1');
connection.release();
}
catch (_) {}
}
use (schema: string, connection?: pg.Client | pg.PoolClient) {

View File

@@ -1,10 +1,11 @@
import * as antares from 'common/interfaces/antares';
import * as sqlite from 'better-sqlite3';
import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/sqlite';
import { NUMBER, FLOAT, TIME, DATETIME } from 'common/fieldTypes';
import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares';
export class SQLiteClient extends AntaresCore {
import { BaseClient } from './BaseClient';
export class SQLiteClient extends BaseClient {
private _schema?: string;
private _connectionsToCommit: Map<string, sqlite.Database>;
protected _connection?: sqlite.Database;
@@ -34,8 +35,8 @@ export class SQLiteClient extends AntaresCore {
});
}
destroy (): void {
return null;
destroy () {
this._connection.close();
}
use (): void {
@@ -295,7 +296,13 @@ export class SQLiteClient extends AntaresCore {
// ADD FIELDS
fields.forEach(field => {
const typeInfo = this.getTypeInfo(field.type);
const length = typeInfo?.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
const length = typeInfo?.length
? field.enumValues ||
field.numLength ||
field.numPrecision ||
field.charLength ||
field.datePrecision
: false;
newColumns.push(`"${field.name}"
${field.type.toUpperCase()}${length ? `(${length})` : ''}
@@ -425,7 +432,7 @@ export class SQLiteClient extends AntaresCore {
return results.rows.map(row => {
return {
sql: row.sql.match(/(?<=AS ).*?$/gs)[0],
sql: row.sql.match(/(?<=(AS|as)( |\n)).*?$/gs)[0],
name: view
};
})[0];

View File

@@ -1,8 +1,8 @@
import * as exporter from 'common/interfaces/exporter';
import * as fs from 'fs';
import { createGzip, Gzip } from 'zlib';
import * as path from 'path';
import * as EventEmitter from 'events';
import * as fs from 'fs';
import * as path from 'path';
import { createGzip, Gzip } from 'zlib';
export class BaseExporter extends EventEmitter {
protected _tables;

View File

@@ -1,9 +1,9 @@
import * as exporter from 'common/interfaces/exporter';
import * as mysql from 'mysql2/promise';
import { SqlExporter } from './SqlExporter';
import { MySQLClient } from '../../clients/MySQLClient';
import { valueToSqlString } from 'common/libs/sqlUtils';
import { MySQLClient } from '../../clients/MySQLClient';
import { SqlExporter } from './SqlExporter';
export default class MysqlExporter extends SqlExporter {
protected _client: MySQLClient;
@@ -333,12 +333,10 @@ CREATE TABLE \`${view.Name}\`(
}
async _queryStream (sql: string) {
if (process.env.NODE_ENV === 'development') console.log('EXPORTER:', sql);
const isPool = 'getConnection' in this._client._connection;
const connection = isPool ? await (this._client._connection as mysql.Pool).getConnection() : this._client._connection;
const connection = await this._client.getConnection();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stream = (connection as any).connection.query(sql).stream();
const dispose = () => (connection as mysql.PoolConnection).release();
const dispose = () => connection.end();
stream.on('end', dispose);
stream.on('error', dispose);

View File

@@ -1,11 +1,12 @@
import * as antares from 'common/interfaces/antares';
import * as exporter from 'common/interfaces/exporter';
import { SqlExporter } from './SqlExporter';
import { valueToSqlString } from 'common/libs/sqlUtils';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import * as QueryStream from 'pg-query-stream';
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
import { valueToSqlString } from 'common/libs/sqlUtils';
import { SqlExporter } from './SqlExporter';
export default class PostgreSQLExporter extends SqlExporter {
constructor (client: PostgreSQLClient, tables: exporter.TableParams[], options: exporter.ExportOptions) {
@@ -424,7 +425,6 @@ SET row_security = off;\n\n\n`;
}
async _queryStream (sql: string) {
if (process.env.NODE_ENV === 'development') console.log('EXPORTER:', sql);
const connection = await this._client.getConnection();
const query = new QueryStream(sql, null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -1,4 +1,5 @@
import * as moment from 'moment';
import { MySQLClient } from '../../clients/MySQLClient';
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
import { BaseExporter } from '../BaseExporter';

View File

@@ -1,6 +1,6 @@
import * as importer from 'common/interfaces/importer';
import * as fs from 'fs';
import * as EventEmitter from 'events';
import * as fs from 'fs';
export class BaseImporter extends EventEmitter {
protected _options;

View File

@@ -1,6 +1,7 @@
import * as mysql from 'mysql2';
import * as importer from 'common/interfaces/importer';
import * as fs from 'fs/promises';
import * as mysql from 'mysql2';
import MySQLParser from '../../parsers/MySQLParser';
import { BaseImporter } from '../BaseImporter';
@@ -32,8 +33,8 @@ export default class MySQLImporter extends BaseImporter {
parser.on('error', reject);
parser.on('close', async () => {
console.log('TOTAL QUERIES', queryCount);
console.log('import end');
// console.log('TOTAL QUERIES', queryCount);
// console.log('import end');
resolve();
});

View File

@@ -1,6 +1,7 @@
import * as pg from 'pg';
import * as importer from 'common/interfaces/importer';
import * as fs from 'fs/promises';
import * as pg from 'pg';
import PostgreSQLParser from '../../parsers/PostgreSQLParser';
import { BaseImporter } from '../BaseImporter';
@@ -32,8 +33,8 @@ export default class PostgreSQLImporter extends BaseImporter {
parser.on('error', reject);
parser.on('close', async () => {
console.log('TOTAL QUERIES', queryCount);
console.log('import end');
// console.log('TOTAL QUERIES', queryCount);
// console.log('import end');
resolve();
});

View File

@@ -0,0 +1,16 @@
import { WebFrameMain } from 'electron';
import * as path from 'path';
const isDevelopment = process.env.NODE_ENV !== 'production';
const isWindows = process.platform === 'win32';
const indexPath = path.resolve(__dirname, 'index.html').split(path.sep).join('/');
export function validateSender (frame: WebFrameMain) {
if (isWindows) return true; // TEMP HOTFIX
const frameUrl = new URL(frame.url);
const prefix = isWindows ? 'file:///' : 'file://';
const framePath = frameUrl.href.replace(prefix, '');
if ((isDevelopment && frameUrl.host === 'localhost:9080') || framePath === indexPath) return true;
return false;
}

View File

@@ -1,13 +1,15 @@
import { app, BrowserWindow, nativeImage, ipcMain } from 'electron';
import * as path from 'path';
import * as remoteMain from '@electron/remote/main';
import { app, BrowserWindow, ipcMain, nativeImage, safeStorage } from 'electron';
import * as log from 'electron-log/main';
import * as Store from 'electron-store';
import * as windowStateKeeper from 'electron-window-state';
import * as remoteMain from '@electron/remote/main';
import * as path from 'path';
import ipcHandlers from './ipc-handlers';
import { OsMenu, ShortcutRegister } from './libs/ShortcutRegister';
Store.initRenderer();
log.errorHandler.startCatching();
const settingsStore = new Store({ name: 'settings' });
const appTheme = settingsStore.get('application_theme');
const isDevelopment = process.env.NODE_ENV !== 'production';
@@ -78,72 +80,80 @@ async function createMainWindow () {
return window;
}
if (!gotTheLock) app.quit();
else {
require('@electron/remote/main').initialize();
require('@electron/remote/main').initialize();
// Initialize ipcHandlers
ipcHandlers();
// Initialize ipcHandlers
ipcHandlers();
ipcMain.on('refresh-theme-settings', () => {
const appTheme = settingsStore.get('application_theme');
if (isWindows && mainWindow) {
mainWindow.setTitleBarOverlay({
color: appTheme === 'dark' ? '#3f3f3f' : '#fff',
symbolColor: appTheme === 'dark' ? '#fff' : '#000'
});
}
});
ipcMain.on('change-window-title', (_, title: string) => {
if (mainWindow) mainWindow.setTitle(title);
});
// quit application when all windows are closed
app.on('window-all-closed', () => {
// on macOS it is common for applications to stay open until the user explicitly quits
if (!isMacOS) app.quit();
});
app.on('activate', async () => {
// on macOS it is common to re-create a window even after all windows have been closed
if (mainWindow === null)
mainWindow = await createMainWindow();
});
// create main BrowserWindow when electron is ready
app.on('ready', async () => {
mainWindowState = windowStateKeeper({
defaultWidth: 1024,
defaultHeight: 800
ipcMain.on('refresh-theme-settings', () => {
const appTheme = settingsStore.get('application_theme');
if (isWindows && mainWindow) {
mainWindow.setTitleBarOverlay({
color: appTheme === 'dark' ? '#3f3f3f' : '#fff',
symbolColor: appTheme === 'dark' ? '#fff' : '#000'
});
}
});
ipcMain.on('change-window-title', (_, title: string) => {
if (mainWindow) mainWindow.setTitle(title);
});
// quit application when all windows are closed
app.on('window-all-closed', () => {
// on macOS it is common for applications to stay open until the user explicitly quits
if (!isMacOS) app.quit();
});
app.on('activate', async () => {
// on macOS it is common to re-create a window even after all windows have been closed
if (mainWindow === null)
mainWindow = await createMainWindow();
createAppMenu();
});
if (isWindows)
mainWindow.show();
// create main BrowserWindow when electron is ready
app.on('ready', async () => {
if (!gotTheLock && !safeStorage.isEncryptionAvailable()) // Disable multiple instances if is not possible to share session keys
app.quit();
// if (isDevelopment)
// mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error);
});
process.on('unhandledRejection', error => {
mainWindow.webContents.send('unhandled-exception', error);
});
mainWindowState = windowStateKeeper({
defaultWidth: 1024,
defaultHeight: 800
});
app.on('browser-window-created', (event, window) => {
if (isDevelopment) {
const { antares } = require('../../package.json');
const extensionPath = path.resolve(__dirname, `../../misc/${antares.devtoolsId}`);
window.webContents.session.loadExtension(extensionPath, { allowFileAccess: true }).catch(console.error);
}
mainWindow = await createMainWindow();
createAppMenu();
if (isWindows)
mainWindow.show();
// if (isDevelopment)
// mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error);
});
}
process.on('unhandledRejection', error => {
mainWindow.webContents.send('unhandled-exception', error);
});
});
app.on('browser-window-created', (event, window) => {
if (isDevelopment) {
const { antares } = require('../../package.json');
const extensionPath = path.resolve(__dirname, `../../misc/${antares.devtoolsId}`);
window.webContents.session.loadExtension(extensionPath, { allowFileAccess: true }).catch(console.error);
}
window.webContents.on('will-navigate', (e) => { // Prevent browser navigation
e.preventDefault();
});
window.webContents.on('did-create-window', (w) => { // Close new windows
w.close();
});
});
function createAppMenu () {
const menuTemplate: OsMenu = {

View File

@@ -1,5 +1,8 @@
import * as antares from 'common/interfaces/antares';
import * as log from 'electron-log/main';
import * as fs from 'fs';
import { parentPort } from 'worker_threads';
import { MySQLClient } from '../libs/clients/MySQLClient';
import { PostgreSQLClient } from '../libs/clients/PostgreSQLClient';
import { ClientsFactory } from '../libs/ClientsFactory';
@@ -7,63 +10,78 @@ import MysqlExporter from '../libs/exporters/sql/MysqlExporter';
import PostgreSQLExporter from '../libs/exporters/sql/PostgreSQLExporter';
let exporter: antares.Exporter;
process.on('message', async ({ type, client, tables, options }: any) => {
log.transports.file.fileName = 'workers.log';
log.transports.console = null;
log.errorHandler.startCatching();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const exportHandler = async (data: any) => {
const { type, client, tables, options } = data;
if (type === 'init') {
const connection = await ClientsFactory.getClient({
client: client.name,
params: client.config,
poolSize: 5
}) as MySQLClient | PostgreSQLClient;
await connection.connect();
try {
const connection = await ClientsFactory.getClient({
client: client.name,
params: client.config,
poolSize: 5
}) as MySQLClient | PostgreSQLClient;
await connection.connect();
switch (client.name) {
case 'mysql':
case 'maria':
exporter = new MysqlExporter(connection as MySQLClient, tables, options);
break;
case 'pg':
exporter = new PostgreSQLExporter(connection as PostgreSQLClient, tables, options);
break;
default:
process.send({
switch (client.name) {
case 'mysql':
case 'maria':
exporter = new MysqlExporter(connection as MySQLClient, tables, options);
break;
case 'pg':
exporter = new PostgreSQLExporter(connection as PostgreSQLClient, tables, options);
break;
default:
parentPort.postMessage({
type: 'error',
payload: `"${client.name}" exporter not aviable`
});
return;
}
exporter.once('error', err => {
log.error(err.toString());
parentPort.postMessage({
type: 'error',
payload: `"${client.name}" exporter not aviable`
payload: err.toString()
});
return;
}
});
exporter.once('error', err => {
console.error(err);
process.send({
exporter.once('end', () => {
parentPort.postMessage({
type: 'end',
payload: { cancelled: exporter.isCancelled }
});
});
exporter.once('cancel', () => {
fs.unlinkSync(exporter.outputFile);
parentPort.postMessage({ type: 'cancel' });
});
exporter.on('progress', state => {
parentPort.postMessage({
type: 'export-progress',
payload: state
});
});
exporter.run();
}
catch (err) {
log.error(err.toString());
parentPort.postMessage({
type: 'error',
payload: err.toString()
});
});
exporter.once('end', () => {
process.send({
type: 'end',
payload: { cancelled: exporter.isCancelled }
});
connection.destroy();
});
exporter.once('cancel', () => {
fs.unlinkSync(exporter.outputFile);
process.send({ type: 'cancel' });
});
exporter.on('progress', state => {
process.send({
type: 'export-progress',
payload: state
});
});
exporter.run();
}
}
else if (type === 'cancel')
exporter.cancel();
});
};
process.on('beforeExit', console.log);
parentPort.on('message', exportHandler);

View File

@@ -1,23 +1,30 @@
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import * as antares from 'common/interfaces/antares';
import * as pg from 'pg';
import { ImportOptions } from 'common/interfaces/importer';
import * as log from 'electron-log/main';
import * as mysql from 'mysql2';
import * as pg from 'pg';
import { parentPort } from 'worker_threads';
import { MySQLClient } from '../libs/clients/MySQLClient';
import { PostgreSQLClient } from '../libs/clients/PostgreSQLClient';
import { ClientsFactory } from '../libs/ClientsFactory';
import MySQLImporter from '../libs/importers/sql/MySQLlImporter';
import PostgreSQLImporter from '../libs/importers/sql/PostgreSQLImporter';
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import { ImportOptions } from 'common/interfaces/importer';
let importer: antares.Importer;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
process.on('message', async ({ type, dbConfig, options }: {
log.transports.file.fileName = 'workers.log';
log.transports.console = null;
log.errorHandler.startCatching();
const importHandler = async (data: {
type: string;
dbConfig: mysql.ConnectionOptions & { schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean }
| pg.ClientConfig & { schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean }
| { databasePath: string; readonly: boolean };
options: ImportOptions;
}) => {
const { type, dbConfig, options } = data;
if (type === 'init') {
try {
const connection = await ClientsFactory.getClient({
@@ -40,7 +47,7 @@ process.on('message', async ({ type, dbConfig, options }: {
importer = new PostgreSQLImporter(pool as unknown as pg.PoolClient, options);
break;
default:
process.send({
parentPort.postMessage({
type: 'error',
payload: `"${options.type}" importer not aviable`
});
@@ -48,33 +55,33 @@ process.on('message', async ({ type, dbConfig, options }: {
}
importer.once('error', err => {
console.error(err);
process.send({
log.error(err.toString());
parentPort.postMessage({
type: 'error',
payload: err.toString()
});
});
importer.once('end', () => {
process.send({
parentPort.postMessage({
type: 'end',
payload: { cancelled: importer.isCancelled }
});
});
importer.once('cancel', () => {
process.send({ type: 'cancel' });
parentPort.postMessage({ type: 'cancel' });
});
importer.on('progress', state => {
process.send({
parentPort.postMessage({
type: 'import-progress',
payload: state
});
});
importer.on('query-error', state => {
process.send({
parentPort.postMessage({
type: 'query-error',
payload: state
});
@@ -83,8 +90,8 @@ process.on('message', async ({ type, dbConfig, options }: {
importer.run();
}
catch (err) {
console.error(err);
process.send({
log.error(err.toString());
parentPort.postMessage({
type: 'error',
payload: err.toString()
});
@@ -92,20 +99,6 @@ process.on('message', async ({ type, dbConfig, options }: {
}
else if (type === 'cancel')
importer.cancel();
});
};
process.on('uncaughtException', (err) => {
console.error(err);
process.send({
type: 'error',
payload: err.toString()
});
});
process.on('unhandledRejection', (err) => {
console.error(err);
process.send({
type: 'error',
payload: err.toString()
});
});
parentPort.on('message', importHandler);

View File

@@ -34,18 +34,19 @@
</template>
<script setup lang="ts">
import { defineAsyncComponent, onMounted, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { getCurrentWindow, Menu } from '@electron/remote';
import { ipcRenderer } from 'electron';
import { storeToRefs } from 'pinia';
import { defineAsyncComponent, onMounted, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Menu, getCurrentWindow } from '@electron/remote';
import ModalExportSchema from '@/components/ModalExportSchema.vue';
import TheSettingBar from '@/components/TheSettingBar.vue';
import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections';
import { useSchemaExportStore } from '@/stores/schemaExport';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import TheSettingBar from '@/components/TheSettingBar.vue';
import ModalExportSchema from '@/components/ModalExportSchema.vue';
const { t } = useI18n();

View File

@@ -47,10 +47,11 @@
</template>
<script setup lang="ts">
import { useFocusTrap } from '@/composables/useFocusTrap';
import { computed, onBeforeUnmount, PropType, useSlots } from 'vue';
import { useI18n } from 'vue-i18n';
import { useFocusTrap } from '@/composables/useFocusTrap';
const { t } = useI18n();
const props = defineProps({

View File

@@ -0,0 +1,62 @@
<template>
<SvgIcon
:type="type"
:path="iconPath"
:size="size"
:rotate="rotate"
:class="iconFlip"
/>
</template>
<script setup lang="ts">
import SvgIcon from '@jamescoyle/vue-icon';
import * as Icons from '@mdi/js';
import { computed, PropType } from 'vue';
const props = defineProps({
iconName: {
type: String,
required: true
},
size: {
type: Number,
default: 48
},
type: {
type: String,
default: () => 'mdi'
},
flip: {
type: String as PropType<'horizontal' | 'vertical' | 'both'>,
default: () => null
},
rotate: {
type: Number,
default: () => null
}
});
const iconPath = computed(() => {
return (Icons as {[k:string]: string})[props.iconName];
});
const iconFlip = computed(() => {
if (['horizontal', 'vertical', 'both'].includes(props.flip))
return `flip-${props.flip}`;
else return '';
});
</script>
<style lang="scss" scoped>
.flip-horizontal {
transform: scaleX(-1);
}
.flip-vertical {
transform: scaleY(-1);
}
.flip-both {
transform: scale(-1, -1);
}
</style>

View File

@@ -3,15 +3,15 @@
</template>
<script setup lang="ts">
import { onMounted, PropType, Ref, ref } from 'vue';
import * as L from 'leaflet';
import {
point,
lineString,
point,
polygon
} from '@turf/helpers';
import { GeoJsonObject } from 'geojson';
import { getArrayDepth } from 'common/libs/getArrayDepth';
import { GeoJsonObject } from 'geojson';
import * as L from 'leaflet';
import { onMounted, PropType, Ref, ref } from 'vue';
interface Coordinates { x: number; y: number }

View File

@@ -1,13 +1,18 @@
<template>
<div class="toast mt-2" :class="notificationStatus.className">
<span class="p-vcentered text-left" :class="{'expanded': isExpanded}">
<i class="mdi mdi-24px mr-2" :class="notificationStatus.iconName" />
<BaseIcon
:icon-name="notificationStatus.iconName"
class="mr-2"
:size="24"
/>
<span class="notification-message">{{ message }}</span>
</span>
<i
<BaseIcon
v-if="isExpandable"
class="mdi mdi-24px c-hand expand-btn"
:class="isExpanded ? 'mdi-chevron-up' : 'mdi-chevron-down'"
:icon-name="isExpanded ? 'mdiChevronUp' : 'mdiChevronDown'"
class=" c-hand expand-btn"
:size="24"
@click="toggleExpand"
/>
<button class="btn btn-clear ml-2" @click="hideToast" />
@@ -17,6 +22,8 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import BaseIcon from '@/components/BaseIcon.vue';
const props = defineProps({
message: {
type: String,
@@ -36,19 +43,19 @@ const notificationStatus = computed(() => {
switch (props.status) {
case 'success':
className = 'toast-success';
iconName = 'mdi-check';
iconName = 'mdiCheck';
break;
case 'error':
className = 'toast-error';
iconName = 'mdi-alert-rhombus';
iconName = 'mdiAlertRhombus';
break;
case 'warning':
className = 'toast-warning';
iconName = 'mdi-alert';
iconName = 'mdiAlert';
break;
case 'primary':
className = 'toast-primary';
iconName = 'mdi-information-outline';
iconName = 'mdiInformationOutline';
break;
}

View File

@@ -70,7 +70,7 @@
</template>
<script>
import { defineComponent, computed, ref, watch, nextTick, onMounted, onUnmounted } from 'vue';
import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
export default defineComponent({
name: 'BaseSelect',

View File

@@ -10,12 +10,16 @@
</template>
<script setup lang="ts">
import { onMounted, watch } from 'vue';
/* eslint-disable simple-import-sort/imports */
import * as ace from 'ace-builds';
import 'ace-builds/webpack-resolver';
import { storeToRefs } from 'pinia';
import { useSettingsStore } from '@/stores/settings';
/* eslint-enable simple-import-sort/imports */
import { uidGen } from 'common/libs/uidGen';
import { storeToRefs } from 'pinia';
import { onMounted, watch } from 'vue';
import { useSettingsStore } from '@/stores/settings';
const props = defineProps({
modelValue: String,
@@ -132,9 +136,4 @@ onMounted(() => {
width: 100%;
}
}
.ace_.mdi {
display: inline-block;
width: 17px;
}
</style>

View File

@@ -1,76 +0,0 @@
<template>
<div
:style="{display: isVisible ? 'flex' : 'none'}"
class="toast mt-2"
:class="toastStatus.className"
>
<span class="p-vcentered text-left"><i class="mdi mdi-24px mr-1" :class="toastStatus.iconName" /> {{ message }}</span>
<button class="btn btn-clear" @click="hideToast" />
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
const props = defineProps({
message: {
type: String,
default: ''
},
status: {
type: String,
default: ''
}
});
const isVisible = ref(false);
const message = ref(props.message);
const emit = defineEmits(['close']);
const toastStatus = computed(() => {
let className = '';
let iconName = '';
switch (props.status) {
case 'success':
className = 'toast-success';
iconName = 'mdi-check';
break;
case 'error':
className = 'toast-error';
iconName = 'mdi-alert-rhombus';
break;
case 'warning':
className = 'toast-warning';
iconName = 'mdi-alert';
break;
case 'primary':
className = 'toast-primary';
iconName = 'mdi-information-outline';
break;
}
return { className, iconName };
});
watch(message, () => {
if (message.value)
isVisible.value = true;
else
isVisible.value = false;
});
const hideToast = () => {
isVisible.value = false;
emit('close');
};
</script>
<style scoped>
.toast {
display: flex;
justify-content: space-between;
user-select: text;
word-break: break-all;
}
</style>

View File

@@ -1,14 +1,20 @@
<template>
<label :for="`id_${id}`" class="file-uploader">
<span class="file-uploader-message">
<i class="mdi mdi-folder-open mr-1" />{{ message }}
<BaseIcon
icon-name="mdiFolderOpen"
class="mr-1 mt-1"
:size="18"
/>{{ message }}
</span>
<span class="text-ellipsis file-uploader-value">
{{ lastPart(modelValue, 19) }}
</span>
<i
<BaseIcon
v-if="modelValue"
class="file-uploader-reset mdi mdi-close"
class="file-upload-icon-clear"
icon-name="mdiClose"
:size="18"
@click.prevent="clear"
/>
<form :ref="`form_${id}`">
@@ -16,6 +22,7 @@
:id="`id_${id}`"
class="file-uploader-input"
type="file"
:accept="accept"
@change="$emit('change', $event)"
>
</form>
@@ -24,6 +31,8 @@
<script setup lang="ts">
import { uidGen } from 'common/libs/uidGen';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFilters } from '@/composables/useFilters';
const { lastPart } = useFilters();
@@ -33,6 +42,10 @@ defineProps({
default: 'Browse',
type: String
},
accept: {
default: '',
type: String
},
modelValue: {
default: '',
type: String
@@ -59,6 +72,12 @@ const clear = () => {
position: relative;
flex: 1 1 auto;
.file-upload-icon-clear {
position: absolute;
right: 4px;
top: 8px;
}
> span {
padding: 0.25rem 0.4rem;
}

View File

@@ -86,13 +86,14 @@
</template>
<script setup lang="ts">
import FakerMethods from 'common/FakerMethods';
import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_BIGINT, LONG_TEXT, NUMBER, TEXT, TIME, UUID } from 'common/fieldTypes';
import { computed, PropType, Ref, ref, watch } from 'vue';
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT, UUID, IS_BIGINT } from 'common/fieldTypes';
import { useI18n } from 'vue-i18n';
import BaseSelect from '@/components/BaseSelect.vue';
import BaseUploadInput from '@/components/BaseUploadInput.vue';
import ForeignKeySelect from '@/components/ForeignKeySelect.vue';
import FakerMethods from 'common/FakerMethods';
import BaseSelect from '@/components/BaseSelect.vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();

View File

@@ -13,15 +13,16 @@
</template>
<script setup lang="ts">
import { computed, Ref, ref, watch } from 'vue';
import { LONG_TEXT, TEXT } from 'common/fieldTypes';
import { TableField } from 'common/interfaces/antares';
import { storeToRefs } from 'pinia';
import { computed, Ref, ref, watch } from 'vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { useFilters } from '@/composables/useFilters';
import Tables from '@/ipc-api/Tables';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { TEXT, LONG_TEXT } from 'common/fieldTypes';
import BaseSelect from '@/components/BaseSelect.vue';
import { TableField } from 'common/interfaces/antares';
import { useFilters } from '@/composables/useFilters';
const props = defineProps({
modelValue: [String, Number],

View File

@@ -9,12 +9,18 @@
@blur="isFocus = false"
@keydown.prevent.stop="onKey"
>
<i class="form-icon mdi mdi-keyboard-outline mdi-24px" />
<BaseIcon
icon-name="mdiKeyboardOutline"
class="form-icon"
:size="24"
/>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, Ref, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import Application from '@/ipc-api/Application';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-apps mr-1" />
<BaseIcon
icon-name="mdiApps"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('connection.allConnections') }}</span>
</div>
</div>
@@ -24,10 +28,17 @@
:placeholder="t('connection.searchForConnections')"
@keypress.esc="searchTerm = ''"
>
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
<i
<BaseIcon
v-if="!searchTerm"
icon-name="mdiMagnify"
class="form-icon pr-4"
:size="18"
/>
<BaseIcon
v-else
class="form-icon c-hand mdi mdi-backspace mdi-18px pr-4"
icon-name="mdiBackspace"
class="form-icon c-hand pr-4"
:size="18"
@click="searchTerm = ''"
/>
</div>
@@ -56,9 +67,11 @@
{{ clients.get(connection.client) || connection.client }}
</div>
<div class="all-connections-buttons p-absolute d-flex" :style="'top: 0; right: 0;'">
<i
class="all-connections-delete mdi mdi-delete mdi-18px ml-2"
<BaseIcon
icon-name="mdiDelete"
class="all-connections-delete ml-2"
:title="t('general.delete')"
:size="18"
@click.stop="askToDelete(connection)"
/>
</div>
@@ -66,32 +79,57 @@
<div class="panel-body text-center">
<div v-if="connection.databasePath">
<div class="text-ellipsis" :title="connection.databasePath">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{
<BaseIcon
icon-name="mdiDatabase"
class="p-relative"
:style="'top:3px'"
:size="18"
/> <span class="text-bold">{{
connection.databasePath
}}</span>
</div>
</div>
<div v-else>
<div class="text-ellipsis" :title="`${connection.host}:${connection.port}`">
<i class="mdi mdi-server d-inline" /> <span class="text-bold">{{ connection.host
<BaseIcon
icon-name="mdiServer"
class="p-relative"
:style="'top:3px'"
:size="18"
/> <span class="text-bold">{{ connection.host
}}:{{ connection.port }}</span>
</div>
</div>
<div v-if="connection.user">
<div class="text-ellipsis">
<i class="mdi mdi-account d-inline" /> <span class="text-bold">{{ connection.user
<BaseIcon
icon-name="mdiAccount"
class="p-relative"
:style="'top:3px'"
:size="18"
/> <span class="text-bold">{{ connection.user
}}</span>
</div>
</div>
<div v-if="connection.schema">
<div class="text-ellipsis">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{ connection.schema
<BaseIcon
icon-name="mdiDatabase"
class="p-relative"
:style="'top:3px'"
:size="18"
/> <span class="text-bold">{{ connection.schema
}}</span>
</div>
</div>
<div v-if="connection.database">
<div class="text-ellipsis">
<i class="mdi mdi-database d-inline" /> <span class="text-bold">{{
<BaseIcon
icon-name="mdiDatabase"
class="p-relative"
:style="'top:3px'"
:size="18"
/> <span class="text-bold">{{
connection.database
}}</span>
</div>
@@ -99,11 +137,19 @@
</div>
<div class="panel-footer text-center py-0">
<div v-if="connection.ssl" class="chip bg-success mt-2">
<i class="mdi mdi-shield-key mdi-18px mr-1" />
<BaseIcon
icon-name="mdiShieldKey"
class="mr-1"
:size="18"
/>
SSL
</div>
<div v-if="connection.ssh" class="chip bg-success mt-2">
<i class="mdi mdi-console-network mdi-18px mr-1" />
<BaseIcon
icon-name="mdiConsoleNetwork"
class="mr-1"
:size="18"
/>
SSH
</div>
</div>
@@ -130,7 +176,11 @@
>
<template #header>
<div class="d-flex">
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ t('connection.deleteConnection') }}
<BaseIcon
icon-name="mdiServerRemove"
class="mr-1"
:size="24"
/> {{ t('connection.deleteConnection') }}
</div>
</template>
<template #body>
@@ -143,14 +193,16 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { ConnectionParams } from 'common/interfaces/antares';
import { storeToRefs } from 'pinia';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { useConnectionsStore } from '@/stores/connections';
import { useWorkspacesStore } from '@/stores/workspaces';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import { ConnectionParams } from 'common/interfaces/antares';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-key-variant mr-1" /> {{ t('connection.credentials') }}
<BaseIcon
icon-name="mdiKeyVariant"
class="mr-1"
:size="24"
/> {{ t('connection.credentials') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
@@ -57,9 +61,11 @@
<script setup lang="ts">
import { Ref, ref } from 'vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
const { t } = useI18n();
const { trapRef } = useFocusTrap();

View File

@@ -8,7 +8,11 @@
>
<template #header>
<div class="d-flex">
<i class="mdi mdi-24px mdi-play mr-1" />
<BaseIcon
icon-name="mdiPlay"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.parameters') }}: {{ localRoutine.name }}</span>
</div>
</template>
@@ -48,13 +52,15 @@
</template>
<script setup lang="ts">
import { computed, PropType, Ref, ref } from 'vue';
import { NUMBER, FLOAT } from 'common/fieldTypes';
import { FLOAT, NUMBER } from 'common/fieldTypes';
import { FunctionInfos, RoutineInfos } from 'common/interfaces/antares';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import { useFilters } from '@/composables/useFilters';
import { computed, PropType, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFilters } from '@/composables/useFilters';
const { t } = useI18n();
const { wrapNumber } = useFilters();

View File

@@ -6,8 +6,12 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-brush-variant mr-1" />
<span class="cut-text">{{ t('connection.editConnectionAppearance') }}</span>
<BaseIcon
icon-name="mdiBrushVariant"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('application.editConnectionAppearance') }}</span>
</div>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
@@ -37,11 +41,24 @@
<div
v-for="icon in icons"
:key="icon.name"
class="icon-box"
:title="icon.name"
:class="[icon.code ? `mdi ${icon.code} mdi-36px` : `dbi dbi-${connection.client}`, {'selected': localConnection.icon === icon.code}]"
@click="localConnection.icon = icon.code"
/>
>
<BaseIcon
v-if="icon.code"
:icon-name="camelize(icon.code)"
:size="36"
class="icon-box"
:title="icon.name"
:class="[{'selected': localConnection.icon === icon.code}]"
@click="localConnection.icon = icon.code"
/>
<div
v-else
class="icon-box"
:title="icon.name"
:class="[`dbi dbi-${connection.client}`, {'selected': localConnection.icon === icon.code}]"
@click="localConnection.icon = icon.code"
/>
</div>
</div>
</div>
</form>
@@ -62,10 +79,12 @@
<script setup lang="ts">
import { onBeforeUnmount, PropType, Ref, ref } from 'vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { useI18n } from 'vue-i18n';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { unproxify } from '@/libs/unproxify';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
const connectionsStore = useConnectionsStore();
@@ -141,6 +160,16 @@ const editFolderAppearance = () => {
closeModal();
};
const camelize = (text: string) => {
const textArr = text.split('-');
for (let i = 0; i < textArr.length; i++) {
if (i === 0) continue;
textArr[i] = textArr[i].charAt(0).toUpperCase() + textArr[i].slice(1);
}
return textArr.join('');
};
const closeModal = () => emit('close');
const onKey =(e: KeyboardEvent) => {

View File

@@ -7,7 +7,11 @@
>
<template #header>
<div class="d-flex">
<i class="mdi mdi-24px mdi-content-save-alert mr-1" /> {{ t('application.unsavedChanges') }}
<BaseIcon
icon-name="mdiContentSaveAlert"
class="mr-1"
:size="24"
/> {{ t('application.unsavedChanges') }}
</div>
</template>
<template #body>
@@ -19,10 +23,12 @@
</template>
<script setup lang="ts">
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import { onBeforeUnmount } from 'vue';
import { useI18n } from 'vue-i18n';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import BaseIcon from '@/components/BaseIcon.vue';
const { t } = useI18n();
const emit = defineEmits(['confirm', 'close']);

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-database-edit mr-1" />
<BaseIcon
icon-name="mdiDatabaseEdit"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.editSchema') }}</span>
</div>
</div>
@@ -64,14 +68,16 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import Schema from '@/ipc-api/Schema';
import BaseSelect from '@/components/BaseSelect.vue';
import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-database-export mr-1" />
<BaseIcon
icon-name="mdiDatabaseExport"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.exportSchema') }}</span>
</div>
</div>
@@ -26,7 +30,7 @@
type="text"
required
readonly
:placeholder="t('database.schemaName')"
@click.prevent="openPathDialog"
>
<button
type="button"
@@ -44,7 +48,11 @@
<div class="column col-8 left">
<div class="columns mb-2 mt-1 p-vcentered">
<div class="column col-auto input-group d-flex text-italic" :style="'flex-grow: 1'">
<i class="input-group-addon mdi mdi-file-document-outline" />
<BaseIcon
icon-name="mdiFileDocumentOutline"
class="input-group-addon"
:size="36"
/>
<input
v-model="chosenFilename"
class="form-input"
@@ -56,27 +64,27 @@
<div class="column col-auto col-ml-auto ">
<button
class="btn btn-dark btn-sm"
class="btn btn-dark btn-sm pt-1"
:title="t('general.refresh')"
@click="refresh"
>
<i class="mdi mdi-refresh" />
<BaseIcon icon-name="mdiRefresh" :size="15" />
</button>
<button
class="btn btn-dark btn-sm mx-1"
class="btn btn-dark btn-sm mx-1 pt-1"
:title="t('database.uncheckAllTables')"
:disabled="isRefreshing"
@click="uncheckAllTables"
>
<i class="mdi mdi-checkbox-blank-outline" />
<BaseIcon icon-name="mdiCheckboxBlankOutline" :size="15" />
</button>
<button
class="btn btn-dark btn-sm"
class="btn btn-dark btn-sm pt-1"
:title="t('database.checkAllTables')"
:disabled="isRefreshing"
@click="checkAllTables"
>
<i class="mdi mdi-checkbox-marked-outline" />
<BaseIcon icon-name="mdiCheckboxMarkedOutline" :size="15" />
</button>
</div>
</div>
@@ -251,7 +259,7 @@
</div>
</div>
<div class="column col-auto px-0">
<button class="btn btn-link" @click.stop="closeModal">
<button class="btn btn-link mr-2" @click.stop="closeModal">
{{ t('general.close') }}
</button>
<button
@@ -271,21 +279,23 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import * as moment from 'moment';
import { ipcRenderer } from 'electron';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { ClientCode, SchemaInfos } from 'common/interfaces/antares';
import { Customizations } from 'common/interfaces/customizations';
import { ExportOptions, ExportState } from 'common/interfaces/exporter';
import { useNotificationsStore } from '@/stores/notifications';
import { useSchemaExportStore } from '@/stores/schemaExport';
import { useWorkspacesStore } from '@/stores/workspaces';
import { ipcRenderer } from 'electron';
import * as moment from 'moment';
import { storeToRefs } from 'pinia';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import Application from '@/ipc-api/Application';
import Schema from '@/ipc-api/Schema';
import { Customizations } from 'common/interfaces/customizations';
import BaseSelect from '@/components/BaseSelect.vue';
import { useNotificationsStore } from '@/stores/notifications';
import { useSchemaExportStore } from '@/stores/schemaExport';
import { useWorkspacesStore } from '@/stores/workspaces';
const emit = defineEmits(['close']);
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
<BaseIcon
icon-name="mdiPlaylistPlus"
:size="24"
class="mr-1"
/>
<span class="cut-text">{{ t('database.insertRow', 2) }}</span>
</div>
</div>
@@ -64,7 +68,7 @@
:disabled="isInserting"
>
<span class="input-group-addon">
<i class="mdi mdi-24px mdi-repeat" />
<BaseIcon icon-name="mdiRepeat" :size="24" />
</span>
</div>
<div
@@ -98,19 +102,21 @@
</template>
<script setup lang="ts">
import { computed, onBeforeMount, onMounted, Prop, Ref, ref, watch } from 'vue';
import * as moment from 'moment';
import { BIT, BLOB, DATE, DATETIME, FLOAT, LONG_TEXT, NUMBER, TEXT, TIME } from 'common/fieldTypes';
import { TableField, TableForeign } from 'common/interfaces/antares';
import * as moment from 'moment';
import { storeToRefs } from 'pinia';
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { computed, onBeforeMount, onMounted, Prop, Ref, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import FakerSelect from '@/components/FakerSelect.vue';
import { useFilters } from '@/composables/useFilters';
import { useFocusTrap } from '@/composables/useFocusTrap';
import Tables from '@/ipc-api/Tables';
import FakerSelect from '@/components/FakerSelect.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { useFilters } from '@/composables/useFilters';
import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-folder-edit mr-1" />
<BaseIcon
icon-name="mdiFolderEdit"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('application.editFolder') }}</span>
</div>
</div>
@@ -43,7 +47,11 @@
:style="`background-color: ${color.hex}`"
@click="localFolder.color = color.hex"
>
<i v-if="localFolder.color === color.hex" class="mdi mdi-check" />
<BaseIcon
v-if="localFolder.color === color.hex"
icon-name="mdiCheck"
:size="16"
/>
</div>
</div>
</div>
@@ -65,10 +73,12 @@
<script setup lang="ts">
import { onBeforeUnmount, PropType, Ref, ref } from 'vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { useI18n } from 'vue-i18n';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { unproxify } from '@/libs/unproxify';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
const connectionsStore = useConnectionsStore();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-history mr-1" />
<BaseIcon
icon-name="mdiHistory"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('general.history') }}: {{ connectionName }}</span>
</div>
</div>
@@ -24,17 +28,24 @@
type="text"
:placeholder="t('database.searchForQueries')"
>
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
<i
<BaseIcon
v-if="!searchTerm"
icon-name="mdiMagnify"
class="form-icon pr-2"
:size="18"
/>
<BaseIcon
v-else
class="form-icon c-hand mdi mdi-backspace mdi-18px pr-4"
icon-name="mdiBackspace"
class="form-icon c-hand pr-2"
:size="18"
@click="searchTerm = ''"
/>
</div>
<div
v-if="history.length"
ref="tableWrapper"
class="vscroll px-1 "
class="vscroll px-1"
:style="{'height': resultsSize+'px'}"
>
<div ref="table">
@@ -53,7 +64,11 @@
tabindex="0"
>
<div class="tile-icon">
<i class="mdi mdi-code-tags pr-1" />
<BaseIcon
icon-name="mdiCodeTags"
class="pr-1"
:size="24"
/>
</div>
<div class="tile-content">
<div class="tile-title">
@@ -67,13 +82,25 @@
<small class="tile-subtitle">{{ query.schema }} · {{ formatDate(query.date) }}</small>
<div class="tile-history-buttons">
<button class="btn btn-link pl-1" @click.stop="$emit('select-query', query.sql)">
<i class="mdi mdi-open-in-app pr-1" /> {{ t('general.select') }}
<BaseIcon
icon-name="mdiOpenInApp"
class="pr-1"
:size="22"
/> {{ t('general.select') }}
</button>
<button class="btn btn-link pl-1" @click="copyQuery(query.sql)">
<i class="mdi mdi-content-copy pr-1" /> {{ t('general.copy') }}
<BaseIcon
icon-name="mdiContentCopy"
class="pr-1"
:size="22"
/> {{ t('general.copy') }}
</button>
<button class="btn btn-link pl-1" @click="deleteQuery(query)">
<i class="mdi mdi-delete-forever pr-1" /> {{ t('general.delete') }}
<BaseIcon
icon-name="mdiDeleteForever"
class="pr-1"
:size="22"
/> {{ t('general.delete') }}
</button>
</div>
</div>
@@ -85,7 +112,7 @@
</div>
<div v-else class="empty">
<div class="empty-icon">
<i class="mdi mdi-history mdi-48px" />
<BaseIcon icon-name="mdiHistory" :size="48" />
</div>
<p class="empty-title h5">
{{ t('database.thereIsNoQueriesYet') }}
@@ -98,14 +125,17 @@
</template>
<script setup lang="ts">
import { ConnectionParams } from 'common/interfaces/antares';
import { Component, computed, ComputedRef, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { ConnectionParams } from 'common/interfaces/antares';
import { HistoryRecord, useHistoryStore } from '@/stores/history';
import { useConnectionsStore } from '@/stores/connections';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { useFilters } from '@/composables/useFilters';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
import { useFilters } from '@/composables/useFilters';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { copyText } from '@/libs/copyText';
import { useConnectionsStore } from '@/stores/connections';
import { HistoryRecord, useHistoryStore } from '@/stores/history';
const { t } = useI18n();
const { formatDate } = useFilters();
@@ -144,7 +174,7 @@ watch(searchTerm, () => {
});
const copyQuery = (sql: string) => {
navigator.clipboard.writeText(sql);
copyText(sql);
};
const deleteQuery = (query: HistoryRecord[]) => {

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-database-import mr-1" />
<BaseIcon
icon-name="mdiDatabaseImport"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.importSchema') }}</span>
</div>
</div>
@@ -50,15 +54,17 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { ImportState } from 'common/interfaces/importer';
import { ipcRenderer } from 'electron';
import * as moment from 'moment';
import { storeToRefs } from 'pinia';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import Schema from '@/ipc-api/Schema';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Schema from '@/ipc-api/Schema';
import { ImportState } from 'common/interfaces/importer';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-database-plus mr-1" />
<BaseIcon
icon-name="mdiDatabasePlus"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.createNewSchema') }}</span>
</div>
</div>
@@ -67,14 +71,16 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import Schema from '@/ipc-api/Schema';
import BaseSelect from '@/components/BaseSelect.vue';
import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
const { t } = useI18n();

View File

@@ -16,7 +16,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-memory mr-1" />
<BaseIcon
icon-name="mdiMemory"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('database.processesList') }}: {{ connectionName }}</span>
</div>
</div>
@@ -32,11 +36,21 @@
:title="`${t('general.refresh')}`"
@click="getProcessesList"
>
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h mr-1" />
<BaseIcon
v-if="!+autorefreshTimer"
icon-name="mdiRefresh"
:size="24"
/>
<BaseIcon
v-else
icon-name="mdiHistory"
flip="horizontal"
class="mr-1"
:size="24"
/>
</button>
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
<i class="mdi mdi-24px mdi-menu-down" />
<BaseIcon icon-name="mdiMenuDown" :size="24" />
</div>
<div class="menu px-3">
<span>{{ t('general.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
@@ -58,9 +72,9 @@
class="btn btn-dark btn-sm dropdown-toggle d-flex mr-0 pr-0"
tabindex="0"
>
<i class="mdi mdi-24px mdi-file-export mr-1" />
<BaseIcon icon-name="mdiFileExport" :size="24" />
<span>{{ t('database.export') }}</span>
<i class="mdi mdi-24px mdi-menu-down" />
<BaseIcon icon-name="mdiMenuDown" :size="24" />
</button>
<ul class="menu text-left">
<li class="menu-item">
@@ -95,10 +109,12 @@
<div ref="columnResize" class="column-resizable">
<div class="table-column-title" @click="sort(field)">
<span>{{ field.toUpperCase() }}</span>
<i
<BaseIcon
v-if="currentSort === field"
class="mdi sort-icon"
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
:icon-name="currentSortDir === 'asc' ? 'mdiSortAscending':'mdiSortDescending'"
:size="18"
class="ml-1"
/>
</div>
</div>
@@ -134,18 +150,22 @@
</template>
<script setup lang="ts">
import { Component, computed, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref } from 'vue';
import { ipcRenderer } from 'electron';
import { ConnectionParams } from 'common/interfaces/antares';
import { exportRows } from '../libs/exportRows';
import { useNotificationsStore } from '@/stores/notifications';
import { ipcRenderer } from 'electron';
import { Component, computed, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
import ModalProcessesListContext from '@/components/ModalProcessesListContext.vue';
import ModalProcessesListRow from '@/components/ModalProcessesListRow.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import Schema from '@/ipc-api/Schema';
import { copyText } from '@/libs/copyText';
import { useConnectionsStore } from '@/stores/connections';
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
import ModalProcessesListRow from '@/components/ModalProcessesListRow.vue';
import ModalProcessesListContext from '@/components/ModalProcessesListContext.vue';
import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications';
import { exportRows } from '../libs/exportRows';
const { t } = useI18n();
@@ -303,13 +323,13 @@ const closeContext = () => {
const copyCell = () => {
const row = results.value.find(row => Number(row.id) === selectedRow.value);
const valueToCopy = row[selectedCell.value.field];
navigator.clipboard.writeText(valueToCopy);
copyText(valueToCopy);
};
const copyRow = () => {
const row = results.value.find(row => Number(row.id) === selectedRow.value);
const rowToCopy = JSON.parse(JSON.stringify(row));
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
copyText(JSON.stringify(rowToCopy));
};
const closeModal = () => emit('close');

View File

@@ -4,8 +4,16 @@
@close-context="closeContext"
>
<div v-if="props.selectedRow" class="context-element">
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ t('general.copy') }}</span>
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
<span class="d-flex"><BaseIcon
icon-name="mdiContentCopy"
class="text-light mt-1 mr-1"
:size="18"
/> {{ t('general.copy') }}</span>
<BaseIcon
icon-name="mdiChevronRight"
class="text-light"
:size="18"
/>
<div class="context-submenu">
<div
v-if="props.selectedRow"
@@ -13,7 +21,12 @@
@click="copyCell"
>
<span class="d-flex">
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ t('database.cell', 1) }}
<BaseIcon
icon-name="mdiNumeric0"
:rotate="90"
class="text-light mt-1 mr-1"
:size="18"
/> {{ t('database.cell', 1) }}
</span>
</div>
<div
@@ -22,7 +35,11 @@
@click="copyRow"
>
<span class="d-flex">
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('database.row', 1) }}
<BaseIcon
icon-name="mdiTableRow"
class="text-light mt-1 mr-1"
:size="18"
/> {{ t('database.row', 1) }}
</span>
</div>
</div>
@@ -33,16 +50,22 @@
@click="killProcess"
>
<span class="d-flex">
<i class="mdi mdi-18px mdi-close-circle-outline text-light pr-1" /> {{ t('database.killProcess') }}
<BaseIcon
icon-name="mdiCloseCircleOutline"
class="text-light mt-1 mr-1"
:size="18"
/> {{ t('database.killProcess') }}
</span>
</div>
</BaseContextMenu>
</template>
<script setup lang="ts">
import BaseContextMenu from '@/components/BaseContextMenu.vue';
import { useI18n } from 'vue-i18n';
import BaseContextMenu from '@/components/BaseContextMenu.vue';
import BaseIcon from '@/components/BaseIcon.vue';
const { t } = useI18n();
const props = defineProps({

View File

@@ -24,7 +24,11 @@
>
<template #header>
<div class="d-flex">
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ t('database.processInfo') }}
<BaseIcon
icon-name="mdiInformationOutline"
:size="24"
class="mr-1"
/> {{ t('database.processInfo') }}
</div>
</template>
<template #body>
@@ -46,10 +50,12 @@
<script setup lang="ts">
import { Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import ConfirmModal from '@/components/BaseConfirmModal.vue';
import BaseIcon from '@/components/BaseIcon.vue';
import TextEditor from '@/components/BaseTextEditor.vue';
import { useFilters } from '@/composables/useFilters';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();

View File

@@ -6,7 +6,11 @@
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-cog mr-1" />
<BaseIcon
icon-name="mdiCog"
class="mr-1"
:size="24"
/>
<span class="cut-text">{{ t('application.settings') }}</span>
</div>
</div>
@@ -37,6 +41,13 @@
>
<a class="tab-link">{{ t('application.shortcuts') }}</a>
</li>
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'data'}"
@click="selectTab('data')"
>
<a class="tab-link">{{ t('application.data') }}</a>
</li>
<li
v-if="updateStatus !== 'disabled'"
class="tab-item c-hand"
@@ -71,7 +82,11 @@
<div class="form-group column col-12">
<div class="col-5 col-sm-12">
<label class="form-label">
<i class="mdi mdi-18px mdi-translate mr-1" />
<BaseIcon
icon-name="mdiTranslate"
class="mr-1"
:size="18"
/>
{{ t('application.language') }}
</label>
</div>
@@ -267,7 +282,11 @@
>
<img :src="darkPreview" class="img-responsive img-fit-cover s-rounded">
<div class="theme-name text-light">
<i class="mdi mdi-moon-waning-crescent mdi-48px" />
<BaseIcon
icon-name="mdiMoonWaningCrescent"
class="mr-1"
:size="48"
/>
<div class="h6 mt-4">
{{ t('application.dark') }}
</div>
@@ -280,7 +299,11 @@
>
<img :src="lightPreview" class="img-responsive img-fit-cover s-rounded">
<div class="theme-name text-dark">
<i class="mdi mdi-white-balance-sunny mdi-48px" />
<BaseIcon
icon-name="mdiWhiteBalanceSunny"
class="mr-1"
:size="48"
/>
<div class="h6 mt-4">
{{ t('application.light') }}
</div>
@@ -366,6 +389,9 @@
<div v-show="selectedTab === 'shortcuts'" class="panel-body py-4">
<ModalSettingsShortcuts />
</div>
<div v-show="selectedTab === 'data'" class="panel-body py-4">
<ModalSettingsData />
</div>
<div v-show="selectedTab === 'update'" class="panel-body py-4">
<ModalSettingsUpdate />
</div>
@@ -379,7 +405,39 @@
<h4>{{ appName }}</h4>
<p class="mb-2">
{{ t('general.version') }} {{ appVersion }}<br>
<a class="c-hand" @click="openOutside('https://github.com/antares-sql/antares')"><i class="mdi mdi-github d-inline" /> GitHub</a> <a class="c-hand" @click="openOutside('https://twitter.com/AntaresSQL')"><i class="mdi mdi-twitter d-inline" /> Twitter</a> <a class="c-hand" @click="openOutside('https://antares-sql.app/')"><i class="mdi mdi-web d-inline" /> Website</a><br>
<a
class="c-hand"
:style="'align-items: center; display: inline-flex;'"
@click="openOutside('https://github.com/antares-sql/antares')"
><BaseIcon
icon-name="mdiGithub"
class="d-inline mr-1"
:size="16"
/> GitHub</a> <a
class="c-hand"
:style="'align-items: center; display: inline-flex;'"
@click="openOutside('https://fosstodon.org/@AntaresSQL')"
><BaseIcon
icon-name="mdiMastodon"
class="d-inline mr-1"
:size="16"
/> Mastodon</a> <a
class="c-hand"
:style="'align-items: center; display: inline-flex;'"
@click="openOutside('https://twitter.com/AntaresSQL')"
><BaseIcon
icon-name="mdiTwitter"
class="d-inline mr-1"
:size="16"
/> Twitter</a> <a
class="c-hand"
:style="'align-items: center; display: inline-flex;'"
@click="openOutside('https://antares-sql.app/')"
><BaseIcon
icon-name="mdiWeb"
class="d-inline mr-1"
:size="16"
/> Website</a><br>
<small>{{ t('general.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">{{ appAuthor }}</a></small><br>
</p>
<div class="mb-2">
@@ -399,21 +457,24 @@
</template>
<script setup lang="ts">
import { onBeforeUnmount, Ref, ref, computed } from 'vue';
import { shell } from 'electron';
import { storeToRefs } from 'pinia';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import BaseTextEditor from '@/components/BaseTextEditor.vue';
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog.vue';
import ModalSettingsData from '@/components/ModalSettingsData.vue';
import ModalSettingsShortcuts from '@/components/ModalSettingsShortcuts.vue';
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { AvailableLocale } from '@/i18n';
import { localesNames } from '@/i18n/supported-locales';
import { useApplicationStore } from '@/stores/application';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { localesNames } from '@/i18n/supported-locales';
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate.vue';
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog.vue';
import ModalSettingsShortcuts from '@/components/ModalSettingsShortcuts.vue';
import BaseTextEditor from '@/components/BaseTextEditor.vue';
import BaseSelect from '@/components/BaseSelect.vue';
import { AvailableLocale } from '@/i18n';
const { t } = useI18n();
@@ -648,10 +709,14 @@ onBeforeUnmount(() => {
.modal-body {
overflow: hidden;
.tab-link {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.tab-item {
max-width: 20%;
.tab-link {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.panel-body {

View File

@@ -8,17 +8,19 @@
/>
<div v-if="isError" class="empty">
<div class="empty-icon">
<i class="mdi mdi-48px mdi-alert-outline" />
<BaseIcon icon-name="mdiAlertOutline" :size="48" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { shell } from 'electron';
import { marked } from 'marked';
import { ref } from 'vue';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseLoader from '@/components/BaseLoader.vue';
import { useApplicationStore } from '@/stores/application';
import { ref } from 'vue';
import { shell } from 'electron';
const { appVersion } = useApplicationStore();

View File

@@ -0,0 +1,69 @@
<template>
<div class="container">
<div class="columns">
<div class="column col-12 h6 text-uppercase mb-2">
{{ t('application.exportData') }}
</div>
<div class="column col-12">
{{ t('application.exportDataExplanation') }}
</div>
<div class="column col-12 text-right">
<button
class="btn btn-primary d-inline-flex"
@click="isExportModal=true"
>
<BaseIcon
icon-name="mdiTrayArrowUp"
class="mr-2"
:size="24"
/>
<span>{{ t('application.exportData') }}</span>
</button>
</div>
</div>
<div class="columns mt-4 mb-2">
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
{{ t('application.importData') }}
</div>
<div class="column col-12">
{{ t('application.importDataExplanation') }}
</div>
<div class="column col-12 text-right">
<button
class="btn btn-dark d-inline-flex ml-auto"
@click="isImportModal=true"
>
<BaseIcon
icon-name="mdiTrayArrowDown"
class="mr-2"
:size="24"
/>
<span>{{ t('application.importData') }}</span>
</button>
</div>
</div>
<ModalSettingsDataExport
v-if="isExportModal"
@close="isExportModal = false"
/>
<ModalSettingsDataImport
v-if="isImportModal"
@close="isImportModal = false"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import ModalSettingsDataExport from '@/components/ModalSettingsDataExport.vue';
import ModalSettingsDataImport from '@/components/ModalSettingsDataImport.vue';
const { t } = useI18n();
const isExportModal = ref(false);
const isImportModal = ref(false);
</script>

View File

@@ -0,0 +1,347 @@
<template>
<Teleport to="#window-content">
<div class="modal active">
<a class="modal-overlay" @click.stop="closeModal" />
<div ref="trapRef" class="modal-container p-0">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<BaseIcon
icon-name="mdiTrayArrowUp"
class="mr-1"
:size="24"
/> {{ t('application.exportData') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div>
<div class="modal-body pb-0">
<div class="columns export-options">
<div class="column col-8 left">
<div class="workspace-query-results" :style="'min-height: 300px;'">
<div ref="table" class="table table-hover">
<div class="thead">
<div class="tr text-center">
<div class="th no-border" :style="'width:50%'" />
<div class="th no-border" />
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllConnections()"
>
<input
type="checkbox"
:indeterminate="includeConnectionStatus === 2"
:checked="!!includeConnectionStatus"
>
<i class="form-icon" />
</label>
</div>
</div>
<div class="tr">
<div class="th">
<div class="table-column-title">
<span>{{ t('connection.connectionName') }}</span>
</div>
</div>
<div class="th">
<div class="table-column-title">
<span>{{ t('connection.client') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ t('general.include') }}</span>
</div>
</div>
</div>
</div>
<div class="tbody">
<div
v-for="(item, i) in connections"
:key="i"
class="tr"
>
<div class="td">
{{ getConnectionName(item.uid) }}
</div>
<div class="td">
{{ item.client }}
</div>
<div class="td text-center">
<label class="form-checkbox m-0 px-2 form-inline">
<input v-model="connectionToggles[item.uid]" type="checkbox">
<i class="form-icon" />
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column col-4">
<h5 class="h5">
{{ t('general.options') }}
</h5>
<label class="form-checkbox">
<input v-model="options.includes.passwords" type="checkbox">
<i class="form-icon" />
{{ t(`application.includeConnectionPasswords`) }}
</label>
<label class="form-checkbox">
<input v-model="options.includes.folders" type="checkbox">
<i class="form-icon" />
{{ t(`application.includeFolders`) }}
</label>
<div class="h6 mt-4 mb-2">
{{ t('application.encryptionPassword') }}
</div>
<fieldset class="form-group" :class="{'has-error': isPasswordError}">
<div class="input-group">
<input
ref="passkey"
v-model="options.passkey"
:type="isPasswordVisible ? 'text' : 'password'"
class="form-input"
:placeholder="t('application.required')"
>
<button
type="button"
class="btn btn-link input-group-addon"
@click="isPasswordVisible = !isPasswordVisible"
>
<BaseIcon
v-if="isPasswordVisible"
icon-name="mdiEye"
class="mt-1 mx-1"
:size="16"
/>
<BaseIcon
v-else
icon-name="mdiEyeOff"
class="mt-1 mx-1"
:size="16"
/>
</button>
</div>
<span v-if="isPasswordError" class="form-input-hint">
{{ t('application.encryptionPasswordError') }}
</span>
</fieldset>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-link mr-2" @click.stop="closeModal">
{{ t('general.close') }}
</button>
<button
class="btn btn-primary mr-2"
autofocus
@click.prevent="exportData()"
>
{{ t('database.export') }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ConnectionParams } from 'common/interfaces/antares';
import { encrypt } from 'common/libs/encrypter';
import { uidGen } from 'common/libs/uidGen';
import * as moment from 'moment';
import { storeToRefs } from 'pinia';
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import { useFocusTrap } from '@/composables/useFocusTrap';
import { unproxify } from '@/libs/unproxify';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
const { t } = useI18n();
const emit = defineEmits(['close']);
const { trapRef } = useFocusTrap();
const { getConnectionName } = useConnectionsStore();
const { connectionsOrder, connections } = storeToRefs(useConnectionsStore());
const localConnections = unproxify<ConnectionParams[]>(connections.value);
const localConnectionsOrder = unproxify<SidebarElement[]>(connectionsOrder.value);
const isPasswordVisible = ref(false);
const isPasswordError = ref(false);
const connectionToggles: Ref<{[k:string]: boolean}> = ref({});
const options = ref({
passkey: '',
includes: {
passwords: true,
folders: true
}
});
const filename = computed(() => {
const date = moment().format('YYYY-MM-DD');
return `backup_${date}`;
});
const includeConnectionStatus = computed(() => {
if (Object.values(connectionToggles.value).every(item => item)) return 1;
else if (Object.values(connectionToggles.value).some(item => item)) return 2;
else return 0;
});
const exportData = () => {
if (options.value.passkey.length < 8)
isPasswordError.value = true;
else {
isPasswordError.value = false;
const connectionsToInclude: string[] = [];
const connectionsUidMap = new Map<string, string>();
for (const cUid in connectionToggles.value)
if (connectionToggles.value[cUid]) connectionsToInclude.push(cUid);
let filteredConnections = unproxify<typeof localConnections>(localConnections.filter(conn => connectionsToInclude.includes(conn.uid)));
filteredConnections = filteredConnections.map(c => {
const newUid = uidGen('C');
connectionsUidMap.set(c.uid, newUid);
c.uid = newUid;
return c;
});
if (!options.value.includes.passwords) { // Remove passwords and set ask:true
filteredConnections.map(c => {
if (c.password) {
c.password = '';
c.ask = true;
}
return c;
});
}
let filteredOrders = [];
for (const [oldVal, newVal] of connectionsUidMap) {
const connOrder = unproxify(localConnectionsOrder.find(c => c.uid === oldVal));
connOrder.uid = newVal;
filteredOrders.push(connOrder);
}
if (options.value.includes.folders) { // Includes folders
const oldConnUids = Array.from(connectionsUidMap.keys());
const newConnUids = Array.from(connectionsUidMap.values());
const foldersToInclude = unproxify(localConnectionsOrder).filter(f => (
f.isFolder && oldConnUids.some(uid => f.connections.includes(uid))
)).map(f => {
f.uid = uidGen('F');
f.connections = f.connections
.map(fc => connectionsUidMap.get(fc))
.filter(fc => newConnUids.includes(fc));
return f;
});
filteredOrders = [...filteredOrders, ...foldersToInclude];
}
const exportObj = encrypt(JSON.stringify({
connections: filteredConnections,
connectionsOrder: filteredOrders
}), options.value.passkey);
// console.log(exportObj, JSON.parse(decrypt(exportObj, options.value.passkey)));
const blobContent = Buffer.from(JSON.stringify(exportObj), 'utf-8').toString('hex');
const file = new Blob([blobContent], { type: 'application/octet-stream' });
const downloadLink = document.createElement('a');
downloadLink.download = `${filename.value}.antares`;
downloadLink.href = window.URL.createObjectURL(file);
downloadLink.style.display = 'none';
document.body.appendChild(downloadLink);
downloadLink.click();
downloadLink.remove();
closeModal();
}
};
const closeModal = () => {
emit('close');
};
const onKey = (e: KeyboardEvent) => {
e.stopPropagation();
if (e.key === 'Escape')
closeModal();
};
const toggleAllConnections = () => {
if (includeConnectionStatus.value !== 1) {
connectionToggles.value = localConnections.reduce((acc, curr) => {
acc[curr.uid] = true;
return acc;
}, {} as {[k:string]: boolean});
}
else {
connectionToggles.value = localConnections.reduce((acc, curr) => {
acc[curr.uid] = false;
return acc;
}, {} as {[k:string]: boolean});
}
};
connectionToggles.value = localConnections.reduce((acc, curr) => {
acc[curr.uid] = true;
return acc;
}, {} as {[k:string]: boolean});
window.addEventListener('keydown', onKey);
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKey);
});
</script>
<style lang="scss" scoped>
.export-options {
flex: 1;
overflow: hidden;
.left {
display: flex;
flex-direction: column;
flex: 1;
}
}
.workspace-query-results {
flex: 1 0 1px;
.table {
width: 100% !important;
}
.form-checkbox {
min-height: 0.8rem;
padding: 0;
.form-icon {
top: 0.1rem;
}
}
}
.modal {
.modal-container {
max-width: 800px;
}
.modal-body {
max-height: 60vh;
display: flex;
flex-direction: column;
}
}
</style>

View File

@@ -0,0 +1,284 @@
<template>
<Teleport to="#window-content">
<div class="modal modal-sm active">
<a class="modal-overlay" @click.stop="closeModal" />
<div ref="trapRef" class="modal-container p-0">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<BaseIcon
icon-name="mdiTrayArrowDown"
class="mr-1"
:size="24"
/> {{ t('application.importData') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div>
<div class="modal-body pb-0">
<div class="mb-2">
<div class="h6 mb-2">
{{ t('application.choseFile') }}
</div>
<BaseUploadInput
:model-value="filePath"
:message="t('general.browse')"
accept=".antares"
@clear="clearPath"
@change="filesChange($event)"
/>
</div>
<div class="mb-2">
<div class="h6 mb-2">
{{ t('application.password') }}
</div>
<fieldset class="form-group" :class="{'has-error': isPasswordError}">
<div class="input-group">
<input
ref="passkey"
v-model="options.passkey"
:type="isPasswordVisible ? 'text' : 'password'"
class="form-input"
:placeholder="t('application.required')"
>
<button
type="button"
class="btn btn-link input-group-addon"
@click="isPasswordVisible = !isPasswordVisible"
>
<BaseIcon
v-if="isPasswordVisible"
icon-name="mdiEye"
class="mt-1 mx-1"
:size="16"
/>
<BaseIcon
v-else
icon-name="mdiEyeOff"
class="mt-1 mx-1"
:size="16"
/>
</button>
</div>
<span v-if="isPasswordError" class="form-input-hint">
{{ t('application.encryptionPasswordError') }}
</span>
</fieldset>
</div>
<div class="mb-2">
<label class="form-checkbox">
<input v-model="options.ignoreDuplicates" type="checkbox">
<i class="form-icon" />
{{ t(`application.ignoreDuplicates`) }}
</label>
</div>
</div>
<div class="modal-footer">
<button
class="btn btn-link mr-2"
@click.stop="closeModal"
>
{{ t('general.close') }}
</button>
<button
class="btn btn-primary mr-2"
:disabled="!filePath"
@click.prevent="importData()"
>
{{ t('database.import') }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ConnectionParams } from 'common/interfaces/antares';
import { decrypt } from 'common/libs/encrypter';
import { storeToRefs } from 'pinia';
import { onBeforeUnmount, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import BaseIcon from '@/components/BaseIcon.vue';
import BaseUploadInput from '@/components/BaseUploadInput.vue';
import { unproxify } from '@/libs/unproxify';
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
const { t } = useI18n();
const emit = defineEmits(['close']);
const { addNotification } = useNotificationsStore();
const connectionsStore = useConnectionsStore();
const { importConnections } = connectionsStore;
const { connections } = storeToRefs(connectionsStore);
const filePath = ref('');
const fileContent = ref(null);
const isPasswordVisible = ref(false);
const isPasswordError = ref(false);
const options = ref({
passkey: '',
ignoreDuplicates: true
});
const closeModal = () => {
emit('close');
};
const filesChange = ({ target } : {target: HTMLInputElement }) => {
const { files } = target;
if (!files.length) return;
const reader = new FileReader();
reader.readAsText(files[0]);
reader.onload = () => {
fileContent.value = reader.result;
filePath.value = files[0].path;
};
};
const clearPath = () => {
filePath.value = '';
fileContent.value = null;
};
const importData = () => {
if (options.value.passkey.length < 8)
isPasswordError.value = true;
else {
try {
const hash = JSON.parse(Buffer.from(fileContent.value, 'hex').toString('utf-8'));
try {
const importObj: {
connections: ConnectionParams[];
connectionsOrder: SidebarElement[];
} = JSON.parse(decrypt(hash, options.value.passkey));
if (options.value.ignoreDuplicates) {
const actualConnections = unproxify(connections.value).map(c => {
delete c.uid;
delete c.name;
delete c.password;
delete c.ask;
delete c.key;
delete c.cert;
delete c.ca;
delete c.sshKey;
return JSON.stringify(c);
});
const incomingConnections = unproxify<ConnectionParams[]>(importObj.connections).map(c => {
const uid = c.uid;
delete c.uid;
delete c.name;
delete c.password;
delete c.ask;
delete c.key;
delete c.cert;
delete c.ca;
delete c.sshKey;
return { uid, jsonString: JSON.stringify(c) };
});
const newConnectionsUid = incomingConnections
.filter(c => !actualConnections.includes(c.jsonString))
.reduce((acc, cur) => {
acc.push(cur.uid);
return acc;
}, [] as string[]);
importObj.connections = importObj.connections.filter(c => newConnectionsUid.includes(c.uid));
importObj.connectionsOrder = importObj.connectionsOrder
.filter(c => newConnectionsUid
.includes(c.uid) ||
(c.isFolder && c.connections.every(c => newConnectionsUid.includes(c))));
}
importConnections(importObj);
addNotification({
status: 'success',
message: t('application.dataImportSuccess')
});
closeModal();
}
catch (error) {
addNotification({
status: 'error',
message: t('application.wrongImportPassword')
});
}
}
catch (error) {
addNotification({
status: 'error',
message: t('application.wrongFileFormat')
});
}
}
};
const onKey = (e: KeyboardEvent) => {
e.stopPropagation();
if (e.key === 'Escape')
closeModal();
};
window.addEventListener('keydown', onKey);
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKey);
});
</script>
<style lang="scss" scoped>
.export-options {
flex: 1;
overflow: hidden;
.left {
display: flex;
flex-direction: column;
flex: 1;
}
}
.workspace-query-results {
flex: 1 0 1px;
.table {
width: 100% !important;
}
.form-checkbox {
min-height: 0.8rem;
padding: 0;
.form-icon {
top: 0.1rem;
}
}
}
.modal {
.modal-body {
max-height: 60vh;
display: flex;
flex-direction: column;
}
}
</style>

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