Merge branch 'master' into feature/translation

This commit is contained in:
Stela Augustinova
2025-11-05 11:56:51 +01:00
65 changed files with 1043 additions and 205 deletions

View File

@@ -6,9 +6,13 @@ name: Electron app BETA
push: push:
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -60,21 +64,53 @@ jobs:
- name: Install Snapcraft - name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1 uses: samuelmeuli/action-snapcraft@v1
- name: Publish - name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

@@ -6,9 +6,13 @@ name: Electron app check build
push: push:
tags: tags:
- check-[0-9]+-[0-9]+-[0-9]+.[0-9]+ - check-[0-9]+-[0-9]+-[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -56,21 +60,53 @@ jobs:
- name: Install Snapcraft - name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1 uses: samuelmeuli/action-snapcraft@v1
- name: Publish - name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

@@ -6,9 +6,13 @@ name: Electron app PREMIUM BETA
push: push:
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -39,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f ref: 6195e103e1d45e4f59bade60df5dd1784f4e6c77
- name: Merge dbgate/dbgate-pro - name: Merge dbgate/dbgate-pro
run: | run: |
mkdir ../dbgate-pro mkdir ../dbgate-pro
@@ -87,23 +91,61 @@ jobs:
cd dbgate-merged cd dbgate-merged
yarn fillPackagedPlugins yarn fillPackagedPlugins
- name: Publish - name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
cd .. cd ..
cd dbgate-merged cd dbgate-merged
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: ../dbgate-merged/app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

@@ -6,9 +6,13 @@ name: Electron app PREMIUM
push: push:
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -39,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f ref: 6195e103e1d45e4f59bade60df5dd1784f4e6c77
- name: Merge dbgate/dbgate-pro - name: Merge dbgate/dbgate-pro
run: | run: |
mkdir ../dbgate-pro mkdir ../dbgate-pro
@@ -87,23 +91,61 @@ jobs:
cd dbgate-merged cd dbgate-merged
yarn fillPackagedPlugins yarn fillPackagedPlugins
- name: Publish - name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
cd .. cd ..
cd dbgate-merged cd dbgate-merged
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: ../dbgate-merged/app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
cd ..
cd dbgate-merged
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

@@ -6,9 +6,13 @@ name: Electron app
push: push:
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -56,24 +60,56 @@ jobs:
- name: Install Snapcraft - name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1 uses: samuelmeuli/action-snapcraft@v1
- name: Publish - name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
yarn run build:app
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
yarn run build:app
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: generatePadFile - name: generatePadFile
run: | run: |
yarn generatePadFile yarn generatePadFile
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: app/dist
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

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

View File

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

View File

@@ -7,6 +7,9 @@ name: NPM packages PREMIUM
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -32,7 +35,7 @@ jobs:
repository: dbgate/dbgate-pro repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f ref: 6195e103e1d45e4f59bade60df5dd1784f4e6c77
- name: Merge dbgate/dbgate-pro - name: Merge dbgate/dbgate-pro
run: | run: |
mkdir ../dbgate-pro mkdir ../dbgate-pro
@@ -49,13 +52,8 @@ jobs:
cd .. cd ..
cd dbgate-merged cd dbgate-merged
node adjustNpmPackageJsonPremium node adjustNpmPackageJsonPremium
- name: Configure NPM token - name: Update npm
env: run: npm install -g npm@latest
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cd ..
cd dbgate-merged
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: Remove dbmodel - should be not published - name: Remove dbmodel - should be not published
run: | run: |
cd .. cd ..
@@ -71,28 +69,35 @@ jobs:
cd .. cd ..
cd dbgate-merged cd dbgate-merged
yarn setCurrentVersion yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish dbgate-api-premium - name: Publish dbgate-api-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/api cd dbgate-merged/packages/api
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-web-premium - name: Publish dbgate-web-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/web cd dbgate-merged/packages/web
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve-premium - name: Publish dbgate-serve-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/serve cd dbgate-merged/packages/serve
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cosmosdb - name: Publish dbgate-plugin-cosmosdb
run: | run: |
cd .. cd ..
cd dbgate-merged/plugins/dbgate-plugin-cosmosdb cd dbgate-merged/plugins/dbgate-plugin-cosmosdb
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-firestore - name: Publish dbgate-plugin-firestore
run: | run: |
cd .. cd ..
cd dbgate-merged/plugins/dbgate-plugin-firestore cd dbgate-merged/plugins/dbgate-plugin-firestore
npm publish npm publish --tag "$NPM_TAG"

View File

@@ -7,6 +7,9 @@ name: NPM packages
tags: tags:
- v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -26,103 +29,107 @@ jobs:
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 22.x node-version: 22.x
- name: Configure NPM token - name: Update npm
env: run: npm install -g npm@latest
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: yarn install - name: yarn install
run: | run: |
yarn install yarn install
- name: setCurrentVersion - name: setCurrentVersion
run: | run: |
yarn setCurrentVersion yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish types - name: Publish types
working-directory: packages/types working-directory: packages/types
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish tools - name: Publish tools
working-directory: packages/tools working-directory: packages/tools
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish sqltree - name: Publish sqltree
working-directory: packages/sqltree working-directory: packages/sqltree
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish api - name: Publish api
working-directory: packages/api working-directory: packages/api
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish datalib - name: Publish datalib
working-directory: packages/datalib working-directory: packages/datalib
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish filterparser - name: Publish filterparser
working-directory: packages/filterparser working-directory: packages/filterparser
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish web - name: Publish web
working-directory: packages/web working-directory: packages/web
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve - name: Publish dbgate-serve
working-directory: packages/serve working-directory: packages/serve
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbmodel - name: Publish dbmodel
working-directory: packages/dbmodel working-directory: packages/dbmodel
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-csv - name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv working-directory: plugins/dbgate-plugin-csv
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-xml - name: Publish dbgate-plugin-xml
working-directory: plugins/dbgate-plugin-xml working-directory: plugins/dbgate-plugin-xml
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-excel - name: Publish dbgate-plugin-excel
working-directory: plugins/dbgate-plugin-excel working-directory: plugins/dbgate-plugin-excel
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mssql - name: Publish dbgate-plugin-mssql
working-directory: plugins/dbgate-plugin-mssql working-directory: plugins/dbgate-plugin-mssql
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mysql - name: Publish dbgate-plugin-mysql
working-directory: plugins/dbgate-plugin-mysql working-directory: plugins/dbgate-plugin-mysql
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mongo - name: Publish dbgate-plugin-mongo
working-directory: plugins/dbgate-plugin-mongo working-directory: plugins/dbgate-plugin-mongo
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-postgres - name: Publish dbgate-plugin-postgres
working-directory: plugins/dbgate-plugin-postgres working-directory: plugins/dbgate-plugin-postgres
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-sqlite - name: Publish dbgate-plugin-sqlite
working-directory: plugins/dbgate-plugin-sqlite working-directory: plugins/dbgate-plugin-sqlite
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-redis - name: Publish dbgate-plugin-redis
working-directory: plugins/dbgate-plugin-redis working-directory: plugins/dbgate-plugin-redis
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-oracle - name: Publish dbgate-plugin-oracle
working-directory: plugins/dbgate-plugin-oracle working-directory: plugins/dbgate-plugin-oracle
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-clickhouse - name: Publish dbgate-plugin-clickhouse
working-directory: plugins/dbgate-plugin-clickhouse working-directory: plugins/dbgate-plugin-clickhouse
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-dbf - name: Publish dbgate-plugin-dbf
working-directory: plugins/dbgate-plugin-dbf working-directory: plugins/dbgate-plugin-dbf
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cassandra - name: Publish dbgate-plugin-cassandra
working-directory: plugins/dbgate-plugin-cassandra working-directory: plugins/dbgate-plugin-cassandra
run: | run: |
npm publish npm publish --tag "$NPM_TAG"

View File

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

View File

@@ -8,12 +8,24 @@ Builds:
- linux - application for linux - linux - application for linux
- win - application for Windows - win - application for Windows
## 6.6.8
- CHANGED: Windows executable now uses Azure trusted signing certificate
- CHANGED: NPM packages now use GitHub OIDC provenance signing for better security
- CHANGED: Some features moved to Premium edition (master/detail views, FK lookups, column expansion, split view, advanced export/import, data archives, grouping, macros)
## 6.6.6
- ADDED: Allow disable/re-enable filter #1174
- ADDED: Close right side tabs #1219
- ADDED: Ability disable execute current line in query editor #1209
- ADDED: Support for Redis Cluster #1204 (Premium)
## 6.6.5 ## 6.6.5
- ADDED: SQL AI assistant - powered by database chat, could help you to write SQL queries (Premium) - ADDED: SQL AI assistant - powered by database chat, could help you to write SQL queries (Premium)
- ADDED: Explain SQL error (powered by AI) (Premium) - ADDED: Explain SQL error (powered by AI) (Premium)
- ADDED: Database chat (and SQL AI Assistant) now supports showing charts (Premium) - ADDED: Database chat (and SQL AI Assistant) now supports showing charts (Premium)
- FIXED: Fxied editing new files and roles (Team Premium) - FIXED: Fxied editing new files and roles (Team Premium)
- FIXED: Connection to standalone database could be now pinned - FIXED: Connection to standalone database could be now pinned
- FIXED: Cannot open up large JSON file #1215
## 6.6.4 ## 6.6.4
- ADDED: AI Database chat now supports much more LLM models. (Premium) - ADDED: AI Database chat now supports much more LLM models. (Premium)

View File

@@ -16,12 +16,9 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application * Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
* **Download** application for Windows, Linux or Mac from [dbgate.io](https://www.dbgate.io/download/) * **Download** application for Windows, Linux or Mac from [dbgate.io](https://www.dbgate.io/download/)
* Looking for DbGate Community? **Download** from [dbgate.org](https://dbgate.org/download/) * Looking for DbGate Community? **Download** from [dbgate.io](https://www.dbgate.io/download-community/)
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate) * Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
* Use nodeJs [scripting interface](https://docs.dbgate.io/scripting) ([API documentation](https://docs.dbgate.io/apidoc)) * Use nodeJs [scripting interface](https://docs.dbgate.io/scripting) ([API documentation](https://docs.dbgate.io/apidoc))
* [Recommend DbGate](https://testimonial.to/dbgate) | [Rate on G2](https://www.g2.com/products/dbgate/reviews)
* [Give us feedback](https://dbgate.org/feedback) - it will help us to decide, how to improve DbGate in future
* We [offer 2-year PREMIUM license](https://dbgate.org/review/) for any honest review on these platforms (time-limited offer)
## Supported databases ## Supported databases
* MySQL * MySQL
@@ -93,7 +90,6 @@ Any contributions are welcome. If you want to contribute without coding, conside
* Tell your friends about DbGate or share on social networks - when more people will use DbGate, it will grow to be better * Tell your friends about DbGate or share on social networks - when more people will use DbGate, it will grow to be better
* Purchase a [DbGate Premium](https://www.dbgate.io/purchase/premium/) license * Purchase a [DbGate Premium](https://www.dbgate.io/purchase/premium/) license
* Write review on [Product Hunt](https://www.producthunt.com/products/dbgate) or [G2](https://www.g2.com/products/dbgate/reviews) - we offer [2-year PREMIUM license](https://dbgate.org/review/) for reviewers (time limited offer)
* Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues * Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues
* Create some tutorial video on [youtube](https://www.youtube.com/playlist?list=PLCo7KjCVXhr0RfUSjM9wJMsp_ShL1q61A) * Create some tutorial video on [youtube](https://www.youtube.com/playlist?list=PLCo7KjCVXhr0RfUSjM9wJMsp_ShL1q61A)
* Become a backer on [GitHub sponsors](https://github.com/sponsors/dbgate) or [Open collective](https://opencollective.com/dbgate) * Become a backer on [GitHub sponsors](https://github.com/sponsors/dbgate) or [Open collective](https://opencollective.com/dbgate)

View File

@@ -117,7 +117,7 @@
"scripts": { "scripts": {
"start": "cross-env ELECTRON_START_URL=http://localhost:5001 DEVMODE=1 electron .", "start": "cross-env ELECTRON_START_URL=http://localhost:5001 DEVMODE=1 electron .",
"start:local": "cross-env electron .", "start:local": "cross-env electron .",
"dist": "electron-builder", "dist": "electron-builder --publish never",
"build": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn dist", "build": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn dist",
"build:local": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn predist", "build:local": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn predist",
"postinstall": "yarn rebuild && patch-package", "postinstall": "yarn rebuild && patch-package",

110
common/fixYmlHashes.js Normal file
View File

@@ -0,0 +1,110 @@
import fs from 'node:fs/promises';
import { createHash } from 'node:crypto';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import YAML from 'yaml';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function sha512Base64(filePath) {
const buf = await fs.readFile(filePath);
const h = createHash('sha512');
h.update(buf);
return h.digest('base64');
}
async function fileSize(filePath) {
const st = await fs.stat(filePath);
return st.size;
}
async function fixOneYaml(ymlPath, distDir) {
let raw;
try {
raw = await fs.readFile(ymlPath, 'utf8');
} catch (e) {
console.error(`✗ Cannot read ${ymlPath}:`, e.message);
return;
}
let doc;
try {
doc = YAML.parse(raw);
} catch (e) {
console.error(`✗ Cannot parse YAML ${ymlPath}:`, e.message);
return;
}
if (!doc || !Array.isArray(doc.files)) {
console.warn(`! ${path.basename(ymlPath)} has no 'files' array — skipped.`);
return;
}
let changed = false;
// Update each files[i].sha512 and files[i].size based on files[i].url
for (const entry of doc.files) {
if (!entry?.url) continue;
const target = path.resolve(distDir, entry.url);
try {
const [hash, size] = await Promise.all([sha512Base64(target), fileSize(target)]);
if (entry.sha512 !== hash || entry.size !== size) {
console.log(`${path.basename(ymlPath)}: refresh ${entry.url}`);
entry.sha512 = hash;
entry.size = size;
changed = true;
}
} catch (e) {
console.warn(
`! Missing or unreadable file for ${entry.url} (referenced by ${path.basename(ymlPath)}): ${e.message}`
);
}
}
// Update top-level sha512 for the primary "path" file if present
if (doc.path) {
const primary = path.resolve(distDir, doc.path);
try {
const hash = await sha512Base64(primary);
if (doc.sha512 !== hash) {
console.log(`${path.basename(ymlPath)}: refresh top-level sha512 for path=${doc.path}`);
doc.sha512 = hash;
changed = true;
}
} catch (e) {
console.warn(`! Primary 'path' file not found for ${path.basename(ymlPath)}: ${doc.path} (${e.message})`);
}
}
if (changed) {
const out = YAML.stringify(doc);
await fs.writeFile(ymlPath, out, 'utf8');
console.log(`✓ Updated ${path.basename(ymlPath)}`);
} else {
console.log(`= No changes for ${path.basename(ymlPath)}`);
}
}
async function main() {
const distDir = path.resolve(process.argv[2] ?? path.join(__dirname, '..', 'app', 'dist'));
const entries = await fs.readdir(distDir);
const ymls = entries.filter(f => f.toLowerCase().endsWith('.yml'));
if (ymls.length === 0) {
console.warn(`No .yml files found in ${distDir}`);
return;
}
console.log(`Scanning ${distDir}`);
for (const y of ymls) {
await fixOneYaml(path.join(distDir, y), distDir);
}
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -110,7 +110,7 @@ describe('Charts', () => {
cy.themeshot('new-object-window'); cy.themeshot('new-object-window');
}); });
it('Database chat - charts', () => { it.only('Database chat - charts', () => {
cy.contains('MySql-connection').click(); cy.contains('MySql-connection').click();
cy.contains('MyChinook').click(); cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click(); cy.testid('TabsPanel_buttonNewObject').click();
@@ -119,8 +119,7 @@ describe('Charts', () => {
cy.get('body').realType('show me chart of most popular genres'); cy.get('body').realType('show me chart of most popular genres');
cy.get('body').realPress('{enter}'); cy.get('body').realPress('{enter}');
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click(); cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.wait(5000); cy.testid('chart-canvas', { timeout: 30000 }).should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
cy.themeshot('database-chat-chart'); cy.themeshot('database-chat-chart');
}); });

View File

@@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "6.6.6-premium-beta.2", "version": "6.6.9",
"name": "dbgate-all", "name": "dbgate-all",
"workspaces": [ "workspaces": [
"packages/*", "packages/*",
@@ -52,6 +52,7 @@
"generatePadFile": "node generatePadFile", "generatePadFile": "node generatePadFile",
"fillPackagedPlugins": "node fillPackagedPlugins", "fillPackagedPlugins": "node fillPackagedPlugins",
"resetPackagedPlugins": "node resetPackagedPlugins", "resetPackagedPlugins": "node resetPackagedPlugins",
"fixYmlHashes": "cd common && yarn init -y && yarn add yaml -W && cd .. && node common/fixYmlHashes.js app/dist",
"prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src", "prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src",
"copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/public/* docker -u 2 && copyfiles \"packages/web/public/**/*\" docker -u 2 && copyfiles \"plugins/dist/**/*\" docker/plugins -u 2", "copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/public/* docker -u 2 && copyfiles \"packages/web/public/**/*\" docker -u 2 && copyfiles \"plugins/dist/**/*\" docker/plugins -u 2",
"copy:packer:build": "copyfiles packages/api/dist/* packer/build -f && copyfiles packages/web/public/* packer/build -u 2 && copyfiles \"packages/web/public/**/*\" packer/build -u 2 && copyfiles \"plugins/dist/**/*\" packer/build/plugins -u 2 && copyfiles packer/install-packages.sh packer/build -f && yarn install:drivers:packer", "copy:packer:build": "copyfiles packages/api/dist/* packer/build -f && copyfiles packages/web/public/* packer/build -u 2 && copyfiles \"packages/web/public/**/*\" packer/build -u 2 && copyfiles \"plugins/dist/**/*\" packer/build/plugins -u 2 && copyfiles packer/install-packages.sh packer/build -f && yarn install:drivers:packer",

View File

@@ -9,6 +9,8 @@ const {
putCloudContent, putCloudContent,
removeCloudCachedConnection, removeCloudCachedConnection,
getPromoWidgetData, getPromoWidgetData,
getPromoWidgetList,
getPromoWidgetPreview,
} = require('../utility/cloudIntf'); } = require('../utility/cloudIntf');
const connections = require('./connections'); const connections = require('./connections');
const socket = require('../utility/socket'); const socket = require('../utility/socket');
@@ -286,7 +288,7 @@ module.exports = {
premiumPromoWidget_meta: true, premiumPromoWidget_meta: true,
async premiumPromoWidget() { async premiumPromoWidget() {
const data = getPromoWidgetData(); const data = await getPromoWidgetData();
if (data?.state != 'data') { if (data?.state != 'data') {
return null; return null;
} }
@@ -296,6 +298,16 @@ module.exports = {
return data; return data;
}, },
promoWidgetList_meta: true,
async promoWidgetList() {
return getPromoWidgetList();
},
promoWidgetPreview_meta: true,
async promoWidgetPreview({ campaign, variant }) {
return getPromoWidgetPreview(campaign, variant);
},
// chatStream_meta: { // chatStream_meta: {
// raw: true, // raw: true,
// method: 'post', // method: 'post',

View File

@@ -274,8 +274,6 @@ module.exports = {
start_meta: true, start_meta: true,
async start({ script }, req) { async start({ script }, req) {
await testStandardPermission('run-shell-script', req);
const runid = crypto.randomUUID(); const runid = crypto.randomUUID();
if (script.type == 'json') { if (script.type == 'json') {
@@ -291,6 +289,8 @@ module.exports = {
return this.startCore(runid, scriptTemplate(js, false)); return this.startCore(runid, scriptTemplate(js, false));
} }
await testStandardPermission('run-shell-script', req);
if (!platformInfo.allowShellScripting) { if (!platformInfo.allowShellScripting) {
sendToAuditLog(req, { sendToAuditLog(req, {
category: 'shell', category: 'shell',

View File

@@ -209,11 +209,11 @@ module.exports = {
return Promise.resolve(); return Promise.resolve();
} }
this.lastPinged[conid] = new Date().getTime(); this.lastPinged[conid] = new Date().getTime();
const opened = await this.ensureOpened(conid);
if (!opened) {
return Promise.resolve();
}
try { try {
const opened = await this.ensureOpened(conid);
if (!opened) {
return Promise.resolve();
}
opened.subprocess.send({ msgtype: 'ping' }); opened.subprocess.send({ msgtype: 'ping' });
} catch (err) { } catch (err) {
logger.error(extractErrorLogData(err), 'DBGM-00121 Error pinging server connection'); logger.error(extractErrorLogData(err), 'DBGM-00121 Error pinging server connection');

View File

@@ -97,6 +97,12 @@ module.exports = {
socket.emit(`session-initialize-file-${jslid}`); socket.emit(`session-initialize-file-${jslid}`);
}, },
handle_changedCurrentDatabase(sesid, props) {
const { database } = props;
this.dispatchMessage(sesid, `Current database changed to ${database}`);
socket.emit(`session-changedb-${sesid}`, { database });
},
handle_ping() {}, handle_ping() {},
create_meta: true, create_meta: true,

View File

@@ -18,6 +18,7 @@ const logger = getLogger('cloudIntf');
let cloudFiles = null; let cloudFiles = null;
let promoWidgetData = null; let promoWidgetData = null;
let promoWidgetDataLoaded = false;
const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY const DBGATE_IDENTITY_URL = process.env.LOCAL_DBGATE_IDENTITY
? 'http://localhost:3103' ? 'http://localhost:3103'
@@ -260,13 +261,21 @@ async function getPublicFileData(path) {
return resp.data; return resp.data;
} }
async function updatePremiumPromoWidget() { async function ensurePromoWidgetDataLoaded() {
if (promoWidgetDataLoaded) {
return;
}
try { try {
const fileContent = await fs.readFile(path.join(datadir(), 'promo-widget.json'), 'utf-8'); const fileContent = await fs.readFile(path.join(datadir(), 'promo-widget.json'), 'utf-8');
promoWidgetData = JSON.parse(fileContent); promoWidgetData = JSON.parse(fileContent);
} catch (err) { } catch (err) {
promoWidgetData = null; promoWidgetData = null;
} }
promoWidgetDataLoaded = true;
}
async function updatePremiumPromoWidget() {
await ensurePromoWidgetDataLoaded();
const tags = (await collectCloudFilesSearchTags()).join(','); const tags = (await collectCloudFilesSearchTags()).join(',');
@@ -466,10 +475,21 @@ async function getPublicIpInfo() {
} }
} }
function getPromoWidgetData() { async function getPromoWidgetData() {
await ensurePromoWidgetDataLoaded();
return promoWidgetData; return promoWidgetData;
} }
async function getPromoWidgetPreview(campaign, variant) {
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/premium-promo-widget-preview/${campaign}/${variant}`);
return resp.data;
}
async function getPromoWidgetList() {
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/promo-widget-list`);
return resp.data;
}
module.exports = { module.exports = {
createDbGateIdentitySession, createDbGateIdentitySession,
startCloudTokenChecking, startCloudTokenChecking,
@@ -488,4 +508,6 @@ module.exports = {
readCloudTestTokenHolder, readCloudTestTokenHolder,
getPublicIpInfo, getPublicIpInfo,
getPromoWidgetData, getPromoWidgetData,
getPromoWidgetPreview,
getPromoWidgetList,
}; };

View File

@@ -148,6 +148,7 @@ class StreamHandler {
// this.error = this.error.bind(this); // this.error = this.error.bind(this);
this.done = this.done.bind(this); this.done = this.done.bind(this);
this.info = this.info.bind(this); this.info = this.info.bind(this);
this.changedCurrentDatabase = this.changedCurrentDatabase.bind(this);
// use this for cancelling - not implemented // use this for cancelling - not implemented
// this.stream = null; // this.stream = null;
@@ -166,6 +167,10 @@ class StreamHandler {
} }
} }
changedCurrentDatabase(database) {
process.send({ msgtype: 'changedCurrentDatabase', database, sesid: this.sesid });
}
recordset(columns) { recordset(columns) {
if (this.rowsLimitOverflow) { if (this.rowsLimitOverflow) {
return; return;

View File

@@ -3,6 +3,10 @@
"name": "dbgate-datalib", "name": "dbgate-datalib",
"main": "lib/index.js", "main": "lib/index.js",
"typings": "lib/index.d.ts", "typings": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"test": "jest", "test": "jest",

View File

@@ -39,7 +39,8 @@ export class TableGridDisplay extends GridDisplay {
public getDictionaryDescription: DictionaryDescriptionFunc = null, public getDictionaryDescription: DictionaryDescriptionFunc = null,
isReadOnly = false, isReadOnly = false,
public isRawMode = false, public isRawMode = false,
public currentSettings = null public currentSettings = null,
public areReferencesAllowed = true
) { ) {
super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion, currentSettings); super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion, currentSettings);
@@ -102,11 +103,11 @@ export class TableGridDisplay extends GridDisplay {
isChecked: this.isColumnChecked(col), isChecked: this.isColumnChecked(col),
hintColumnNames: hintColumnNames:
this.getFkDictionaryDescription(col.isForeignKeyUnique ? col.foreignKey : null)?.columns?.map(columnName => this.getFkDictionaryDescription(col.isForeignKeyUnique ? col.foreignKey : null)?.columns?.map(columnName =>
shortenIdentifier(`hint_${col.uniqueName}_${columnName}`, this.driver.dialect.maxIdentifierLength) shortenIdentifier(`hint_${col.uniqueName}_${columnName}`, this.driver?.dialect?.maxIdentifierLength)
) || null, ) || null,
hintColumnDelimiter: this.getFkDictionaryDescription(col.isForeignKeyUnique ? col.foreignKey : null) hintColumnDelimiter: this.getFkDictionaryDescription(col.isForeignKeyUnique ? col.foreignKey : null)
?.delimiter, ?.delimiter,
uniqueNameShorten: shortenIdentifier(col.uniqueName, this.driver.dialect.maxIdentifierLength), uniqueNameShorten: shortenIdentifier(col.uniqueName, this.driver?.dialect?.maxIdentifierLength),
isExpandable: !!col.foreignKey, isExpandable: !!col.foreignKey,
})) || [] })) || []
); );
@@ -117,7 +118,7 @@ export class TableGridDisplay extends GridDisplay {
if (this.isExpandedColumn(column.uniqueName)) { if (this.isExpandedColumn(column.uniqueName)) {
const table = this.getFkTarget(column); const table = this.getFkTarget(column);
if (table) { if (table) {
const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver.dialect.maxIdentifierLength); const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver?.dialect?.maxIdentifierLength);
const subcolumns = this.getDisplayColumns(table, column.uniquePath); const subcolumns = this.getDisplayColumns(table, column.uniquePath);
this.addReferenceToSelect(select, parentAlias, column); this.addReferenceToSelect(select, parentAlias, column);
@@ -130,7 +131,7 @@ export class TableGridDisplay extends GridDisplay {
} }
addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) { addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) {
const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver.dialect.maxIdentifierLength); const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver?.dialect?.maxIdentifierLength);
if ((select.from.relations || []).find(x => x.alias == childAlias)) return; if ((select.from.relations || []).find(x => x.alias == childAlias)) return;
const table = this.getFkTarget(column); const table = this.getFkTarget(column);
if (table && table.primaryKey) { if (table && table.primaryKey) {
@@ -195,11 +196,11 @@ export class TableGridDisplay extends GridDisplay {
this.addReferenceToSelect( this.addReferenceToSelect(
select, select,
parentUniqueName parentUniqueName
? shortenIdentifier(`${parentUniqueName}_ref`, this.driver.dialect.maxIdentifierLength) ? shortenIdentifier(`${parentUniqueName}_ref`, this.driver?.dialect?.maxIdentifierLength)
: 'basetbl', : 'basetbl',
column column
); );
const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver.dialect.maxIdentifierLength); const childAlias = shortenIdentifier(`${column.uniqueName}_ref`, this.driver?.dialect?.maxIdentifierLength);
select.columns.push( select.columns.push(
...hintDescription.columns.map( ...hintDescription.columns.map(
columnName => columnName =>
@@ -208,7 +209,7 @@ export class TableGridDisplay extends GridDisplay {
columnName, columnName,
alias: shortenIdentifier( alias: shortenIdentifier(
`hint_${column.uniqueName}_${columnName}`, `hint_${column.uniqueName}_${columnName}`,
this.driver.dialect.maxIdentifierLength this.driver?.dialect?.maxIdentifierLength
), ),
source: { alias: childAlias }, source: { alias: childAlias },
} as ColumnRefExpression) } as ColumnRefExpression)
@@ -248,6 +249,7 @@ export class TableGridDisplay extends GridDisplay {
} }
processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo, options) { processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo, options) {
if (!this.areReferencesAllowed) return;
this.addJoinsFromExpandedColumns(select, this.columns, 'basetbl', displayedColumnInfo); this.addJoinsFromExpandedColumns(select, this.columns, 'basetbl', displayedColumnInfo);
if (!options.isExport && this.displayOptions.showHintColumns) { if (!options.isExport && this.displayOptions.showHintColumns) {
this.addHintsToSelect(select); this.addHintsToSelect(select);

View File

@@ -3,6 +3,10 @@
"name": "dbgate-filterparser", "name": "dbgate-filterparser",
"main": "lib/index.js", "main": "lib/index.js",
"typings": "lib/index.d.ts", "typings": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"start": "tsc --watch", "start": "tsc --watch",

View File

@@ -1,4 +1,5 @@
import _cloneDeep from 'lodash/cloneDeep'; import _cloneDeep from 'lodash/cloneDeep';
import _uniq from 'lodash/uniq';
import _isString from 'lodash/isString'; import _isString from 'lodash/isString';
import type { import type {
ColumnInfo, ColumnInfo,
@@ -75,9 +76,27 @@ export function findForeignKeyForColumn(table: TableInfo, column: ColumnInfo | s
return (table.foreignKeys || []).find(fk => fk.columns.find(col => col.columnName == column.columnName)); return (table.foreignKeys || []).find(fk => fk.columns.find(col => col.columnName == column.columnName));
} }
export function getConflictingColumnNames(columns: ColumnInfo[]): Set<string> {
const conflictingNames = new Set(
_uniq(columns.map(x => x.columnName).filter((item, index, arr) => arr.indexOf(item) !== index))
);
return conflictingNames;
}
export function makeUniqueColumnNames(res: ColumnInfo[]) { export function makeUniqueColumnNames(res: ColumnInfo[]) {
const usedNames = new Set(); const usedNames = new Set();
const conflictingNames = getConflictingColumnNames(res);
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {
if (
conflictingNames.has(res[i].columnName) &&
res[i].pureName &&
!usedNames.has(`${res[i].pureName}_${res[i].columnName}`)
) {
res[i].columnName = `${res[i].pureName}_${res[i].columnName}`;
usedNames.add(res[i].columnName);
continue;
}
if (usedNames.has(res[i].columnName)) { if (usedNames.has(res[i].columnName)) {
let suffix = 2; let suffix = 2;
while (usedNames.has(`${res[i].columnName}${suffix}`)) suffix++; while (usedNames.has(`${res[i].columnName}${suffix}`)) suffix++;

View File

@@ -21,6 +21,7 @@ export interface StreamOptions {
error?: (error) => void; error?: (error) => void;
done?: (result) => void; done?: (result) => void;
info?: (info) => void; info?: (info) => void;
changedCurrentDatabase?: (database: string) => void;
} }
export type CollectionOperationInfo = export type CollectionOperationInfo =

View File

@@ -9,6 +9,10 @@
"build:index": "node build-index.js", "build:index": "node build-index.js",
"prepublishOnly": "yarn build" "prepublishOnly": "yarn build"
}, },
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"files": [ "files": [
"public" "public"
], ],

View File

@@ -30,3 +30,20 @@
.color-icon-inv-red { .color-icon-inv-red {
color: var(--theme-icon-inv-red); color: var(--theme-icon-inv-red);
} }
.premium-background-gradient {
background: linear-gradient(135deg, #1686c8, #8a25b1);
}
.premium-gradient {
background: linear-gradient(135deg, #1686c8, #8a25b1);
color: white;
}
.web-color-primary {
background: #1686c8;
}
.web-color-secondary {
background: #8a25b1;
}

View File

@@ -125,8 +125,8 @@
{/if} {/if}
<div class="purchase-info"> <div class="purchase-info">
For more info about DbGate licensing, you could visit <Link href="https://dbgate.eu/">dbgate.eu</Link> web or contact For more info about DbGate licensing, you could visit <Link href="https://dbgate.io/">dbgate.io</Link> web or contact
us at <Link href="mailto:sales@dbgate.eu">sales@dbgate.eu</Link> us at <Link href="mailto:sales@dbgate.io">sales@dbgate.io</Link>
</div> </div>
{:else} {:else}
<ErrorInfo message="License for DbGate is not valid. Please contact administrator." /> <ErrorInfo message="License for DbGate is not valid. Please contact administrator." />

View File

@@ -16,7 +16,7 @@
<div class="heading">Configuration error</div> <div class="heading">Configuration error</div>
{#if $config?.checkedLicense?.status == 'error'} {#if $config?.checkedLicense?.status == 'error'}
<ErrorInfo <ErrorInfo
message={`Invalid license. Please contact sales@dbgate.eu for more details. ${$config?.checkedLicense?.error}`} message={`Invalid license. Please contact sales@dbgate.io for more details. ${$config?.checkedLicense?.error}`}
/> />
{:else if $config?.configurationError} {:else if $config?.configurationError}
<ErrorInfo message={$config?.configurationError} /> <ErrorInfo message={$config?.configurationError} />

View File

@@ -468,12 +468,14 @@ await dbgateApi.executeQuery(${JSON.stringify(
{ divider: true }, { divider: true },
isSqlOrDoc && isSqlOrDoc &&
isProApp() &&
!connection.isReadOnly && !connection.isReadOnly &&
hasPermission(`dbops/import`) && { hasPermission(`dbops/import`) && {
onClick: handleImport, onClick: handleImport,
text: _t('database.import', { defaultMessage: 'Import' }), text: _t('database.import', { defaultMessage: 'Import' }),
}, },
isSqlOrDoc && isSqlOrDoc &&
isProApp() &&
hasPermission(`dbops/export`) && { hasPermission(`dbops/export`) && {
onClick: handleExport, onClick: handleExport,
text: _t('database.export', { defaultMessage: 'Export' }), text: _t('database.export', { defaultMessage: 'Export' }),

View File

@@ -9,6 +9,7 @@
export let title = null; export let title = null;
export let skipWidth = false; export let skipWidth = false;
export let outline = false; export let outline = false;
export let colorClass = '';
function handleClick() { function handleClick() {
if (!disabled) dispatch('click'); if (!disabled) dispatch('click');
@@ -31,6 +32,8 @@
bind:this={domButton} bind:this={domButton}
class:skipWidth class:skipWidth
class:outline class:outline
class={colorClass}
class:setBackgroundColor={!colorClass}
/> />
<style> <style>
@@ -38,19 +41,26 @@
border: 1px solid var(--theme-bg-button-inv-2); border: 1px solid var(--theme-bg-button-inv-2);
padding: 5px; padding: 5px;
margin: 2px; margin: 2px;
background-color: var(--theme-bg-button-inv);
color: var(--theme-font-inv-1); color: var(--theme-font-inv-1);
border-radius: 2px; border-radius: 2px;
} }
.setBackgroundColor {
background-color: var(--theme-bg-button-inv);
}
input:not(.skipWidth) { input:not(.skipWidth) {
width: 100px; width: 100px;
} }
input:hover:not(.disabled):not(.outline) { input:not(.setBackgroundColor) {
cursor: pointer;
}
input.setBackgroundColor:hover:not(.disabled):not(.outline) {
background-color: var(--theme-bg-button-inv-2); background-color: var(--theme-bg-button-inv-2);
} }
input:active:not(.disabled):not(.outline) { input.setBackgroundColor:active:not(.disabled):not(.outline) {
background-color: var(--theme-bg-button-inv-3); background-color: var(--theme-bg-button-inv-3);
} }
input.disabled { input.disabled {

View File

@@ -8,6 +8,7 @@ import {
getCloudSigninTokenHolder, getCloudSigninTokenHolder,
getExtensions, getExtensions,
getVisibleToolbar, getVisibleToolbar,
promoWidgetPreview,
visibleToolbar, visibleToolbar,
visibleWidgetSideBar, visibleWidgetSideBar,
} from '../stores'; } from '../stores';
@@ -50,6 +51,7 @@ import { isProApp } from '../utility/proTools';
import { openWebLink } from '../utility/simpleTools'; import { openWebLink } from '../utility/simpleTools';
import { _t } from '../translations'; import { _t } from '../translations';
import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte'; import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte';
import { getBoolSettingsValue } from '../settings/settingsTools';
// function themeCommand(theme: ThemeDefinition) { // function themeCommand(theme: ThemeDefinition) {
// return { // return {
@@ -689,7 +691,7 @@ registerCommand({
name: 'Export database', name: 'Export database',
toolbar: true, toolbar: true,
icon: 'icon export', icon: 'icon export',
testEnabled: () => getCurrentDatabase() != null && hasPermission(`dbops/export`), testEnabled: () => getCurrentDatabase() != null && hasPermission(`dbops/export`) && isProApp(),
onClick: () => { onClick: () => {
openImportExportTab({ openImportExportTab({
targetStorageType: getDefaultFileFormat(getExtensions()).storageType, targetStorageType: getDefaultFileFormat(getExtensions()).storageType,
@@ -1164,6 +1166,41 @@ registerCommand({
onClick: () => currentDatabase.set(null), onClick: () => currentDatabase.set(null),
}); });
let loadedCampaignList = [];
registerCommand({
id: 'internal.loadCampaigns',
category: 'Internal',
name: 'Load campaign list',
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false),
onClick: async () => {
const resp = await apiCall('cloud/promo-widget-list', {});
loadedCampaignList = resp;
},
});
registerCommand({
id: 'internal.showCampaigns',
category: 'Internal',
name: 'Show campaigns',
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false) && loadedCampaignList?.length > 0,
getSubCommands: () => {
return loadedCampaignList.map(campaign => ({
text: `${campaign.campaignName} (${campaign.countries || 'Global'}) - #${campaign.quantileRank ?? '*'}/${
campaign.quantileGroupCount ?? '*'
} - ${campaign.variantIdentifier}`,
onClick: async () => {
promoWidgetPreview.set(
await apiCall('cloud/promo-widget-preview', {
campaign: campaign.campaignIdentifier,
variant: campaign.variantIdentifier,
})
);
},
}));
},
});
const electron = getElectron(); const electron = getElectron();
if (electron) { if (electron) {
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId)); electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));

View File

@@ -12,6 +12,7 @@
import { showModal } from '../modals/modalTools'; import { showModal } from '../modals/modalTools';
import DefineDictionaryDescriptionModal from '../modals/DefineDictionaryDescriptionModal.svelte'; import DefineDictionaryDescriptionModal from '../modals/DefineDictionaryDescriptionModal.svelte';
import { sleep } from '../utility/common'; import { sleep } from '../utility/common';
import { isProApp } from '../utility/proTools';
export let column; export let column;
export let conid = undefined; export let conid = undefined;
@@ -72,29 +73,35 @@
column.foreignKey && [{ divider: true }, { onClick: openReferencedTable, text: column.foreignKey.refTableName }], column.foreignKey && [{ divider: true }, { onClick: openReferencedTable, text: column.foreignKey.refTableName }],
setGrouping && { divider: true }, isProApp() &&
setGrouping && { onClick: () => setGrouping('GROUP'), text: 'Group by' }, setGrouping && [
setGrouping && { onClick: () => setGrouping('MAX'), text: 'MAX' }, { divider: true },
setGrouping && { onClick: () => setGrouping('MIN'), text: 'MIN' }, { onClick: () => setGrouping('GROUP'), text: 'Group by' },
setGrouping && { onClick: () => setGrouping('SUM'), text: 'SUM' }, { onClick: () => setGrouping('MAX'), text: 'MAX' },
setGrouping && { onClick: () => setGrouping('AVG'), text: 'AVG' }, { onClick: () => setGrouping('MIN'), text: 'MIN' },
setGrouping && { onClick: () => setGrouping('COUNT'), text: 'COUNT' }, { onClick: () => setGrouping('SUM'), text: 'SUM' },
setGrouping && { onClick: () => setGrouping('COUNT DISTINCT'), text: 'COUNT DISTINCT' }, { onClick: () => setGrouping('AVG'), text: 'AVG' },
{ onClick: () => setGrouping('COUNT'), text: 'COUNT' },
{ onClick: () => setGrouping('COUNT DISTINCT'), text: 'COUNT DISTINCT' },
],
isTypeDateTime(column.dataType) && [ isProApp() &&
{ divider: true }, isTypeDateTime(column.dataType) && [
{ onClick: () => setGrouping('GROUP:YEAR'), text: 'Group by YEAR' }, { divider: true },
{ onClick: () => setGrouping('GROUP:MONTH'), text: 'Group by MONTH' }, { onClick: () => setGrouping('GROUP:YEAR'), text: 'Group by YEAR' },
{ onClick: () => setGrouping('GROUP:DAY'), text: 'Group by DAY' }, { onClick: () => setGrouping('GROUP:MONTH'), text: 'Group by MONTH' },
], { onClick: () => setGrouping('GROUP:DAY'), text: 'Group by DAY' },
],
{ divider: true }, { divider: true },
allowDefineVirtualReferences && { onClick: handleDefineVirtualForeignKey, text: 'Define virtual foreign key' }, isProApp() &&
column.foreignKey && { allowDefineVirtualReferences && { onClick: handleDefineVirtualForeignKey, text: 'Define virtual foreign key' },
onClick: handleCustomizeDescriptions, column.foreignKey &&
text: 'Customize description', isProApp() && {
}, onClick: handleCustomizeDescriptions,
text: 'Customize description',
},
]; ];
} }
</script> </script>

View File

@@ -7,6 +7,7 @@
import { showModal } from '../modals/modalTools'; import { showModal } from '../modals/modalTools';
import ColumnEditorModal from '../tableeditor/ColumnEditorModal.svelte'; import ColumnEditorModal from '../tableeditor/ColumnEditorModal.svelte';
import { editorDeleteColumn } from 'dbgate-tools'; import { editorDeleteColumn } from 'dbgate-tools';
import { isProApp } from '../utility/proTools';
export let column; export let column;
export let display; export let display;
@@ -59,13 +60,17 @@
on:mouseup on:mouseup
> >
<div> <div>
<span class="expandColumnIcon" style={`margin-right: ${5 + (column.uniquePath.length - 1) * 10}px`}> {#if isProApp()}
<FontIcon <span class="expandColumnIcon" style={`margin-right: ${5 + (column.uniquePath.length - 1) * 10}px`}>
icon={column.isExpandable ? plusExpandIcon(display.isExpandedColumn(column.uniqueName)) : 'icon invisible-box'} <FontIcon
on:click={() => display.toggleExpandedColumn(column.uniqueName)} icon={column.isExpandable
data-testid="ColumnManagerRow_expand_{column.uniqueName}" ? plusExpandIcon(display.isExpandedColumn(column.uniqueName))
/> : 'icon invisible-box'}
</span> on:click={() => display.toggleExpandedColumn(column.uniqueName)}
data-testid="ColumnManagerRow_expand_{column.uniqueName}"
/>
</span>
{/if}
{#if isJsonView} {#if isJsonView}
<FontIcon icon="img column" /> <FontIcon icon="img column" />
{:else} {:else}

View File

@@ -69,6 +69,7 @@
import { registerMenu } from '../utility/contextMenu'; import { registerMenu } from '../utility/contextMenu';
import { getLocalStorage, setLocalStorage } from '../utility/storageCache'; import { getLocalStorage, setLocalStorage } from '../utility/storageCache';
import { __t, _t } from '../translations'; import { __t, _t } from '../translations';
import { isProApp } from '../utility/proTools';
export let config; export let config;
export let setConfig; export let setConfig;
@@ -206,7 +207,7 @@
name="references" name="references"
height="30%" height="30%"
collapsed={isDetailView} collapsed={isDetailView}
skip={!(showReferences && display?.hasReferences)} skip={!(showReferences && display?.hasReferences && isProApp())}
data-testid="DataGrid_itemReferences" data-testid="DataGrid_itemReferences"
> >
<ReferenceManager {...$$props} {managerSize} /> <ReferenceManager {...$$props} {managerSize} />
@@ -215,7 +216,7 @@
<WidgetColumnBarItem <WidgetColumnBarItem
title={_t('dataGrid.macros', { defaultMessage: 'Macros' })} title={_t('dataGrid.macros', { defaultMessage: 'Macros' })}
name="macros" name="macros"
skip={!showMacros} skip={!(showMacros && isProApp())}
collapsed={!expandMacros} collapsed={!expandMacros}
data-testid="DataGrid_itemMacros" data-testid="DataGrid_itemMacros"
> >

View File

@@ -1413,7 +1413,11 @@
function handleGridWheel(event) { function handleGridWheel(event) {
if (event.shiftKey) { if (event.shiftKey) {
scrollHorizontal(event.deltaY, event.deltaX); if (isMac()) {
scrollHorizontal(event.deltaX, event.deltaY);
} else {
scrollHorizontal(event.deltaY, event.deltaX);
}
} else { } else {
scrollHorizontal(event.deltaX, event.deltaY); scrollHorizontal(event.deltaX, event.deltaY);
scrollVertical(event.deltaX, event.deltaY); scrollVertical(event.deltaX, event.deltaY);

View File

@@ -55,7 +55,7 @@
{/if} {/if}
{#if dependencies.length > 0} {#if dependencies.length > 0}
<div class="bold nowrap ml-1">Dependend tables ({dependencies.length})</div> <div class="bold nowrap ml-1">Dependent tables ({dependencies.length})</div>
{#each dependencies.filter(fk => filterName(filter, fk.pureName)) as fk} {#each dependencies.filter(fk => filterName(filter, fk.pureName)) as fk}
<div <div
class="link" class="link"

View File

@@ -30,6 +30,7 @@
import SqlFormView from '../formview/SqlFormView.svelte'; import SqlFormView from '../formview/SqlFormView.svelte';
import { getBoolSettingsValue } from '../settings/settingsTools'; import { getBoolSettingsValue } from '../settings/settingsTools';
import { getDictionaryDescription } from '../utility/dictionaryDescriptionTools'; import { getDictionaryDescription } from '../utility/dictionaryDescriptionTools';
import { isProApp } from '../utility/proTools';
export let conid; export let conid;
export let database; export let database;
@@ -82,7 +83,8 @@
extendedDbInfo?.tables?.find(x => x.pureName == pureName && x.schemaName == schemaName) extendedDbInfo?.tables?.find(x => x.pureName == pureName && x.schemaName == schemaName)
?.tablePermissionRole == 'read', ?.tablePermissionRole == 'read',
isRawMode, isRawMode,
$settingsValue $settingsValue,
isProApp()
) )
: null; : null;

View File

@@ -1,22 +1,31 @@
<script lang="ts"> <script lang="ts">
import JsonUiCountdown from './JsonUiCountdown.svelte';
import JsonUiHeading from './JsonUiHeading.svelte'; import JsonUiHeading from './JsonUiHeading.svelte';
import JsonUiHighlight from './JsonUiHighlight.svelte';
import JsonUiLinkButton from './JsonUiLinkButton.svelte'; import JsonUiLinkButton from './JsonUiLinkButton.svelte';
import JsonUiLinkButtonBlock from './JsonUiLinkButtonBlock.svelte';
import JsonUiMarkdown from './JsonUiMarkdown.svelte';
import JsonUiTextBlock from './JsonUiTextBlock.svelte'; import JsonUiTextBlock from './JsonUiTextBlock.svelte';
import JsonUiTickList from './JsonUiTickList.svelte'; import JsonUiTickList from './JsonUiTickList.svelte';
import { JsonUiBlock } from './jsonuitypes'; import { JsonUiBlock } from './jsonuitypes';
export let blocks: JsonUiBlock[] = []; export let blocks: JsonUiBlock[] = [];
export let passProps = {};
const componentMap = { const componentMap = {
text: JsonUiTextBlock, text: JsonUiTextBlock,
heading: JsonUiHeading, heading: JsonUiHeading,
ticklist: JsonUiTickList, ticklist: JsonUiTickList,
button: JsonUiLinkButton, button: JsonUiLinkButton,
markdown: JsonUiMarkdown,
highlight: JsonUiHighlight,
countdown: JsonUiCountdown,
buttonblock: JsonUiLinkButtonBlock,
} as const; } as const;
</script> </script>
{#each blocks as block, i} {#each blocks as block, i}
{#if block.type in componentMap} {#if block.type in componentMap}
<svelte:component this={componentMap[block.type]} {...block} /> <svelte:component this={componentMap[block.type]} {...block} {...passProps} />
{/if} {/if}
{/each} {/each}

View File

@@ -0,0 +1,87 @@
<script lang="ts">
import { onMount } from 'svelte';
import { openWebLink } from '../utility/simpleTools';
export let colorClass: string = 'premium-gradient';
export let validTo;
export let link;
function formatRemaining(validTo, now) {
let diffMs = validTo.getTime() - now.getTime();
if (diffMs <= 0) return '0 minutes';
const totalMinutes = Math.floor(diffMs / 60000);
const days = Math.floor(totalMinutes / (24 * 60));
const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
const minutes = totalMinutes % 60;
const parts = [];
const en = (n, unit) => ({
num: n,
unit: n == 1 ? unit : unit + 's',
});
if (days) parts.push(en(days, 'day'));
if (hours) parts.push(en(hours, 'hour'));
// Always include minutes to report down to minutes
parts.push(en(minutes, 'minute'));
return parts;
}
let currentDate = new Date();
onMount(() => {
const interval = setInterval(() => {
currentDate = new Date();
}, 5000);
return () => {
clearInterval(interval);
};
});
$: parts = formatRemaining(new Date(validTo), currentDate);
</script>
{#if validTo}
<div
class="countdown {colorClass}"
class:isLink={!!link}
on:click={() => {
if (link) {
openWebLink(link);
}
}}
>
<span class="big">Offer ends in:</span><br />
{#each parts as part}
<span class="part">
<span class="big">{part.num}</span>
{part.unit}
</span>
{/each}
</div>
{/if}
<style>
.countdown {
text-align: center;
margin: 10px;
border: 1px solid;
padding: 5px;
}
.countdown.isLink {
cursor: pointer;
}
.big {
font-size: large;
font-weight: bold;
}
.part {
margin: 0 5px;
}
</style>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import { openWebLink } from '../utility/simpleTools';
export let text: string;
export let colorClass: string = 'premium-gradient';
export let link: string;
</script>
<div
class="highlight {colorClass}"
class:isLink={!!link}
on:click={() => {
if (link) {
openWebLink(link);
}
}}
>
{text}
</div>
<style>
.highlight {
display: block;
text-align: center;
margin: 10px;
font-size: large;
font-weight: bold;
border: 1px solid;
padding: 5px;
}
.highlight.isLink {
cursor: pointer;
}
</style>

View File

@@ -4,14 +4,11 @@
export let text: string; export let text: string;
export let link: string; export let link: string;
export let newTab: boolean = true; export let colorClass: string = '';
// very light url guard
const safe = /^(https?:)?\/\//i.test(link) || link.startsWith('/');
</script> </script>
<div class="center"> <div class="center">
<FormStyledButton on:click={() => openWebLink(link)} value={text} skipWidth /> <FormStyledButton on:click={() => openWebLink(link)} value={text} skipWidth {colorClass} />
</div> </div>
<style> <style>

View File

@@ -0,0 +1,21 @@
<script lang="ts">
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import { openWebLink } from '../utility/simpleTools';
export let text: string;
export let link: string;
export let colorClass: string = '';
export let items: any[] = [];
</script>
<div class="center">
{#each items as item}
<FormStyledButton on:click={() => openWebLink(item.link)} value={item.text} skipWidth {colorClass} />
{/each}
</div>
<style>
.center {
text-align: center;
}
</style>

View File

@@ -0,0 +1,15 @@
<script lang="ts">
import Markdown from '../elements/Markdown.svelte';
export let text: string;
</script>
<div>
<Markdown source={text} />
</div>
<style>
div {
margin: 10px;
}
</style>

View File

@@ -95,6 +95,7 @@
title: _t('common.exportDatabase', { defaultMessage: 'Export database' }), title: _t('common.exportDatabase', { defaultMessage: 'Export database' }),
description: _t('newObject.exportDescription', { defaultMessage: 'Export to file like CSV, JSON, Excel, or other DB' }), description: _t('newObject.exportDescription', { defaultMessage: 'Export to file like CSV, JSON, Excel, or other DB' }),
command: 'database.export', command: 'database.export',
isProFeature: true,
testid: 'NewObjectModal_databaseExport', testid: 'NewObjectModal_databaseExport',
disabledMessage: _t('newObject.exportDisabled', { defaultMessage: 'Export is not available for current database' }), disabledMessage: _t('newObject.exportDisabled', { defaultMessage: 'Export is not available for current database' }),
}, },

View File

@@ -187,6 +187,9 @@ export const seenPremiumPromoWidget = writableWithStorage(null, 'seenPremiumProm
export const cloudConnectionsStore = writable({}); export const cloudConnectionsStore = writable({});
export const promoWidgetPreview = writable(null);
export const DEFAULT_OBJECT_SEARCH_SETTINGS = { export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
pureName: true, pureName: true,
schemaName: false, schemaName: false,

View File

@@ -358,6 +358,8 @@
import { handleAfterTabClick } from '../utility/changeCurrentDbByTab'; import { handleAfterTabClick } from '../utility/changeCurrentDbByTab';
import { getBoolSettingsValue } from '../settings/settingsTools'; import { getBoolSettingsValue } from '../settings/settingsTools';
import NewObjectModal from '../modals/NewObjectModal.svelte'; import NewObjectModal from '../modals/NewObjectModal.svelte';
import { isProApp } from '../utility/proTools';
import { openWebLink } from '../utility/simpleTools';
export let multiTabIndex; export let multiTabIndex;
export let shownTab; export let shownTab;
@@ -583,7 +585,13 @@
</script> </script>
<div class="root"> <div class="root">
<div class="tabs" class:can-split={allowSplitTab} on:wheel={handleTabsWheel} bind:this={domTabs}> <div
class="tabs"
class:can-split={allowSplitTab && isProApp()}
class:tabs-upgrade-button={!isProApp()}
on:wheel={handleTabsWheel}
bind:this={domTabs}
>
{#each groupedTabs as tabGroup} {#each groupedTabs as tabGroup}
<div class="db-wrapper"> <div class="db-wrapper">
{#if !$lockedDatabaseMode} {#if !$lockedDatabaseMode}
@@ -713,7 +721,7 @@
{/each} {/each}
</div> </div>
<div class="icons-wrapper"> <div class="icons-wrapper">
{#if allowSplitTab} {#if allowSplitTab && isProApp()}
<div <div
class="icon-button" class="icon-button"
on:click={() => splitTab(multiTabIndex)} on:click={() => splitTab(multiTabIndex)}
@@ -723,6 +731,22 @@
<FontIcon icon="icon split" /> <FontIcon icon="icon split" />
</div> </div>
{/if} {/if}
{#if !isProApp()}
<div
class="upgrade-button"
on:click={() => {
openWebLink(
`https://www.dbgate.io/purchase/${isElectronAvailable() ? 'premium' : 'team-premium'}/?utm_campaign=premiumUpgradeButton`
);
}}
title="Upgrade to Premium"
data-testid="TabsPanel_buttonUpgrade"
>
<FontIcon icon="icon premium" padRight /> Upgrade
</div>
{/if}
<div <div
class="icon-button" class="icon-button"
on:click={() => showModal(NewObjectModal, { multiTabIndex })} on:click={() => showModal(NewObjectModal, { multiTabIndex })}
@@ -756,6 +780,18 @@
color: var(--theme-font-2); color: var(--theme-font-2);
cursor: pointer; cursor: pointer;
} }
.upgrade-button {
background: linear-gradient(135deg, #1686c8, #8a25b1);
border: 1px solid var(--theme-border);
border-radius: 10px;
color: white;
cursor: pointer;
font-size: 10pt;
padding: 5px;
}
.upgrade-button:hover {
background: linear-gradient(135deg, #0f5a85, #5c1870);
}
.icon-button:hover { .icon-button:hover {
color: var(--theme-font-1); color: var(--theme-font-1);
} }
@@ -769,6 +805,10 @@
right: 35px; right: 35px;
bottom: 0; bottom: 0;
} }
.tabs-upgrade-button {
right: 120px;
}
.tabs.can-split { .tabs.can-split {
right: 60px; right: 60px;
} }

View File

@@ -266,9 +266,11 @@
if (sid) { if (sid) {
apiOn(`session-done-${sid}`, handleSessionDone); apiOn(`session-done-${sid}`, handleSessionDone);
apiOn(`session-closed-${sid}`, handleSessionClosed); apiOn(`session-closed-${sid}`, handleSessionClosed);
apiOn(`session-changedb-${sid}`, handleChangedDatabase);
return () => { return () => {
apiOff(`session-done-${sid}`, handleSessionDone); apiOff(`session-done-${sid}`, handleSessionDone);
apiOff(`session-closed-${sid}`, handleSessionClosed); apiOff(`session-closed-${sid}`, handleSessionClosed);
apiOff(`session-changedb-${sid}`, handleChangedDatabase);
}; };
} }
return () => {}; return () => {};
@@ -567,6 +569,17 @@
handleSessionDone(); handleSessionDone();
}; };
const handleChangedDatabase = async props => {
changeTab(tabid, tab => ({
...tab,
props: {
...tab.props,
conid,
database: props.database,
},
}));
};
const { editorState, editorValue, setEditorData } = useEditorData({ const { editorState, editorValue, setEditorData } = useEditorData({
tabid, tabid,
loadFromArgs: loadFromArgs:

View File

@@ -2,11 +2,12 @@ import type { QuickExportDefinition } from 'dbgate-types';
import { currentArchive, getCurrentArchive, getExtensions } from '../stores'; import { currentArchive, getCurrentArchive, getExtensions } from '../stores';
import hasPermission from './hasPermission'; import hasPermission from './hasPermission';
import { _t } from '../translations' import { _t } from '../translations'
import { isProApp } from './proTools';
export function createQuickExportMenuItems(handler: (fmt: QuickExportDefinition) => Function, advancedExportMenuItem) { export function createQuickExportMenuItems(handler: (fmt: QuickExportDefinition) => Function, advancedExportMenuItem) {
const extensions = getExtensions(); const extensions = getExtensions();
return [ return [
{ isProApp() && {
text: _t('export.exportAdvanced', { defaultMessage : 'Export advanced...'}), text: _t('export.exportAdvanced', { defaultMessage : 'Export advanced...'}),
...advancedExportMenuItem, ...advancedExportMenuItem,
}, },
@@ -16,7 +17,7 @@ export function createQuickExportMenuItems(handler: (fmt: QuickExportDefinition)
onClick: handler(fmt), onClick: handler(fmt),
})), })),
{ divider: true }, { divider: true },
{ isProApp() && {
text: _t('export.currentArchive', { defaultMessage : 'Current archive'}), text: _t('export.currentArchive', { defaultMessage : 'Current archive'}),
onClick: handler({ onClick: handler({
extension: 'jsonl', extension: 'jsonl',

View File

@@ -1,14 +1,17 @@
<script lang="ts"> <script lang="ts">
import JsonUiContentRenderer from '../jsonui/JsonUiContentRenderer.svelte'; import JsonUiContentRenderer from '../jsonui/JsonUiContentRenderer.svelte';
import { promoWidgetPreview } from '../stores';
import { usePromoWidget } from '../utility/metadataLoaders'; import { usePromoWidget } from '../utility/metadataLoaders';
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte'; import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
const promoWidget = usePromoWidget({}); const promoWidget = usePromoWidget({});
$: promoWidgetData = $promoWidgetPreview || $promoWidget;
</script> </script>
<WidgetsInnerContainer> <WidgetsInnerContainer>
{#if $promoWidget?.state == 'data'} {#if promoWidgetData?.state == 'data'}
<JsonUiContentRenderer blocks={$promoWidget?.blocks} /> <JsonUiContentRenderer blocks={promoWidgetData?.blocks} passProps={{ validTo: promoWidgetData?.validTo }} />
{/if} {/if}
</WidgetsInnerContainer> </WidgetsInnerContainer>

View File

@@ -27,6 +27,7 @@
import { useConnectionColor } from '../utility/useConnectionColor'; import { useConnectionColor } from '../utility/useConnectionColor';
import { apiCall } from '../utility/api'; import { apiCall } from '../utility/api';
import { statusBarTabInfo } from '../utility/statusBarStore'; import { statusBarTabInfo } from '../utility/statusBarStore';
import { isProApp } from '../utility/proTools';
$: databaseName = $currentDatabase && $currentDatabase.name; $: databaseName = $currentDatabase && $currentDatabase.name;
$: connection = $currentDatabase && $currentDatabase.connection; $: connection = $currentDatabase && $currentDatabase.connection;
@@ -155,7 +156,7 @@
</div> </div>
</div> </div>
{/if} {/if}
{#if $currentArchive && $currentArchive != 'default'} {#if isProApp() && $currentArchive && $currentArchive != 'default'}
<div <div
class="item flex clickable" class="item flex clickable"
title="Current archive" title="Current archive"

View File

@@ -11,6 +11,7 @@
import PublicCloudWidget from './PublicCloudWidget.svelte'; import PublicCloudWidget from './PublicCloudWidget.svelte';
import PrivateCloudWidget from './PrivateCloudWidget.svelte'; import PrivateCloudWidget from './PrivateCloudWidget.svelte';
import hasPermission from '../utility/hasPermission'; import hasPermission from '../utility/hasPermission';
import { isProApp } from '../utility/proTools';
</script> </script>
{#if hasPermission('widgets/database')} {#if hasPermission('widgets/database')}
@@ -22,7 +23,7 @@
{#if $visibleSelectedWidget == 'history' && hasPermission('widgets/history')} {#if $visibleSelectedWidget == 'history' && hasPermission('widgets/history')}
<HistoryWidget /> <HistoryWidget />
{/if} {/if}
{#if $visibleSelectedWidget == 'archive' && hasPermission('widgets/archive')} {#if $visibleSelectedWidget == 'archive' && hasPermission('widgets/archive') && isProApp()}
<ArchiveWidget /> <ArchiveWidget />
{/if} {/if}
{#if $visibleSelectedWidget == 'plugins' && hasPermission('widgets/plugins')} {#if $visibleSelectedWidget == 'plugins' && hasPermission('widgets/plugins')}

View File

@@ -11,6 +11,7 @@
getCurrentConfig, getCurrentConfig,
cloudSigninTokenHolder, cloudSigninTokenHolder,
seenPremiumPromoWidget, seenPremiumPromoWidget,
promoWidgetPreview,
} from '../stores'; } from '../stores';
import mainMenuDefinition from '../../../../app/src/mainMenuDefinition'; import mainMenuDefinition from '../../../../app/src/mainMenuDefinition';
import hasPermission from '../utility/hasPermission'; import hasPermission from '../utility/hasPermission';
@@ -60,7 +61,7 @@
name: 'history', name: 'history',
title: 'Query history & Closed tabs', title: 'Query history & Closed tabs',
}, },
{ isProApp() && {
icon: 'icon archive', icon: 'icon archive',
name: 'archive', name: 'archive',
title: 'Archive (saved tabular data)', title: 'Archive (saved tabular data)',
@@ -167,6 +168,8 @@
openWebLink(url, true); openWebLink(url, true);
} }
} }
$: promoWidgetData = $promoWidgetPreview || $promoWidget;
</script> </script>
<div class="main"> <div class="main">
@@ -177,7 +180,7 @@
{/if} {/if}
{#each widgets {#each widgets
.filter(x => x && hasPermission(`widgets/${x.name}`)) .filter(x => x && hasPermission(`widgets/${x.name}`))
.filter(x => !x.isPremiumPromo || (!isProApp() && $promoWidget?.state == 'data')) .filter(x => !x.isPremiumPromo || (!isProApp() && promoWidgetData?.state == 'data'))
// .filter(x => !x.isPremiumOnly || isProApp()) // .filter(x => !x.isPremiumOnly || isProApp())
.filter(x => x.name != 'cloud-private' || $cloudSigninTokenHolder) as item} .filter(x => x.name != 'cloud-private' || $cloudSigninTokenHolder) as item}
<div <div
@@ -186,10 +189,18 @@
data-testid={`WidgetIconPanel_${item.name}`} data-testid={`WidgetIconPanel_${item.name}`}
on:click={() => handleChangeWidget(item.name)} on:click={() => handleChangeWidget(item.name)}
> >
<FontIcon icon={item.icon} title={item.title} /> {#if item.isPremiumPromo && promoWidgetData?.isColoredIcon}
<FontIcon
icon={item.icon}
title={item.title}
colorClass="premium-background-gradient widget-icon-panel-rounded"
/>
{:else}
<FontIcon icon={item.icon} title={item.title} />
{/if}
{#if item.isPremiumPromo} {#if item.isPremiumPromo}
<div class="premium-promo">Premium</div> <div class="premium-promo">Premium</div>
{#if $promoWidget?.identifier != $seenPremiumPromoWidget} {#if promoWidgetData?.identifier != $seenPremiumPromoWidget}
<div class="premium-promo-not-seen">•</div> <div class="premium-promo-not-seen">•</div>
{/if} {/if}
{/if} {/if}
@@ -281,4 +292,9 @@
top: -5px; top: -5px;
right: 5px; right: 5px;
} }
:global(.widget-icon-panel-rounded) {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
</style> </style>

View File

@@ -5,6 +5,10 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"author": "Jan Prochazka", "author": "Jan Prochazka",
"description": "cassandra connector for DbGate", "description": "cassandra connector for DbGate",
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"keywords": [ "keywords": [
"dbgate", "dbgate",
"cassandra", "cassandra",

View File

@@ -14,6 +14,10 @@
"dist", "dist",
"icon.svg" "icon.svg"
], ],
"repository": {
"type": "git",
"url": "https://github.com/dbgate/dbgate.git"
},
"scripts": { "scripts": {
"build:frontend": "webpack --config webpack-frontend.config", "build:frontend": "webpack --config webpack-frontend.config",
"build:frontend:watch": "webpack --watch --config webpack-frontend.config", "build:frontend:watch": "webpack --watch --config webpack-frontend.config",

View File

@@ -186,7 +186,11 @@ async function tediousStream(dbhan, sql, options) {
severity: 'error', severity: 'error',
}); });
}; };
const handleDatabaseChange = database => {
options.changedCurrentDatabase(database);
};
dbhan.client.on('databaseChange', handleDatabaseChange);
dbhan.client.on('infoMessage', handleInfo); dbhan.client.on('infoMessage', handleInfo);
dbhan.client.on('errorMessage', handleError); dbhan.client.on('errorMessage', handleError);
const request = new tedious.Request(sql, (err, rowCount) => { const request = new tedious.Request(sql, (err, rowCount) => {

View File

@@ -3,6 +3,7 @@ const stream = require('stream');
const driverBases = require('../frontend/drivers'); const driverBases = require('../frontend/drivers');
const Analyser = require('./Analyser'); const Analyser = require('./Analyser');
const mysql2 = require('mysql2'); const mysql2 = require('mysql2');
const fs = require('fs');
const { getLogger, createBulkInsertStreamBase, makeUniqueColumnNames, extractErrorLogData } = const { getLogger, createBulkInsertStreamBase, makeUniqueColumnNames, extractErrorLogData } =
global.DBGATE_PACKAGES['dbgate-tools']; global.DBGATE_PACKAGES['dbgate-tools'];
@@ -14,6 +15,7 @@ function extractColumns(fields) {
if (fields) { if (fields) {
const res = fields.map(col => ({ const res = fields.map(col => ({
columnName: col.name, columnName: col.name,
pureName: col.orgTable,
})); }));
makeUniqueColumnNames(res); makeUniqueColumnNames(res);
return res; return res;
@@ -53,6 +55,7 @@ const drivers = driverBases.map(driverBase => ({
supportBigNumbers: true, supportBigNumbers: true,
bigNumberStrings: true, bigNumberStrings: true,
dateStrings: true, dateStrings: true,
infileStreamFactory: path => fs.createReadStream(path),
// TODO: test following options // TODO: test following options
// multipleStatements: true, // multipleStatements: true,
}; };
@@ -127,6 +130,9 @@ const drivers = driverBases.map(driverBase => ({
time: new Date(), time: new Date(),
severity: 'info', severity: 'info',
}); });
if (row.stateChanges?.schema) {
options.changedCurrentDatabase(row.stateChanges.schema);
}
} else { } else {
if (columns) { if (columns) {
options.row(zipDataRow(row, columns)); options.row(zipDataRow(row, columns));

View File

@@ -7,7 +7,7 @@
"homepage": "https://dbgate.org", "homepage": "https://dbgate.org",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/rinie/dbgate-plugin-oracle" "url": "https://github.com/dbgate/dbgate.git"
}, },
"author": "Rinie Kervel", "author": "Rinie Kervel",
"keywords": [ "keywords": [

View File

@@ -13,6 +13,7 @@ const {
makeUniqueColumnNames, makeUniqueColumnNames,
extractDbNameFromComposite, extractDbNameFromComposite,
extractErrorLogData, extractErrorLogData,
getConflictingColumnNames,
} = global.DBGATE_PACKAGES['dbgate-tools']; } = global.DBGATE_PACKAGES['dbgate-tools'];
let authProxy; let authProxy;
@@ -60,7 +61,23 @@ function extractPostgresColumns(result, dbhan) {
columnName: fld.name, columnName: fld.name,
dataTypeId: fld.dataTypeID, dataTypeId: fld.dataTypeID,
dataTypeName: typeIdToName[fld.dataTypeID], dataTypeName: typeIdToName[fld.dataTypeID],
tableId: fld.tableID,
})); }));
// const conflictingNames = getConflictingColumnNames(res);
// if (conflictingNames.size > 0) {
// const requiredTableIds = res.filter(x => conflictingNames.has(x.columnName)).map(x => x.tableId);
// const tableIdResult = await dbhan.client.query(
// `SELECT DISTINCT c.oid AS table_id, c.relname AS table_name
// FROM pg_class c
// WHERE c.oid IN (${requiredTableIds.join(',')})`
// );
// const tableIdToTableName = _.fromPairs(tableIdResult.rows.map(cur => [cur.table_id, cur.table_name]));
// for (const col of res) {
// col.pureName = tableIdToTableName[col.tableId];
// }
// }
makeUniqueColumnNames(res); makeUniqueColumnNames(res);
return res; return res;
} }

View File

@@ -85,9 +85,14 @@ on:
# branches: # branches:
# - production # - production
permissions:
id-token: write
contents: write
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment: dbgate-app
strategy: strategy:
fail-fast: false fail-fast: false
@@ -145,33 +150,71 @@ jobs:
_if: _community _if: _community
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1 uses: samuelmeuli/action-snapcraft@v1
- name: Publish
- name: Publish Windows
if: matrix.os == 'windows-2022'
run: |
<<cd_merged>>
yarn run build:app
# env:
# GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
- name: Publish MacOS
if: matrix.os == 'macos-14'
run: | run: |
<<cd_merged>> <<cd_merged>>
yarn run build:app yarn run build:app
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish # GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}} APPLE_APP_SPECIFIC_PASSWORD: ${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}
- name: Publish Linux
if: matrix.os == 'ubuntu-22.04'
run: |
<<cd_merged>>
yarn run build:app
env:
# GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: generatePadFile - name: generatePadFile
_if: _community_stable _if: _community_stable
run: | run: |
yarn generatePadFile yarn generatePadFile
- name: Azure login (OIDC)
uses: azure/login@v2
if: matrix.os == 'windows-2022'
with:
client-id: ${{ secrets.AZURE_TC_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TC_TENANT_ID }}
allow-no-subscriptions: true
- name: Sign Windows artifacts with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
if: matrix.os == 'windows-2022'
with:
endpoint: https://wus3.codesigning.azure.net/
trusted-signing-account-name: DbGate
certificate-profile-name: DbGate-Release
files-folder: <<artifact-root>>
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Fix YML hashes
if: matrix.os == 'windows-2022'
run: |
<<cd_merged>>
yarn run fixYmlHashes
- name: Copy artifacts - name: Copy artifacts
run: | run: |
mkdir artifacts mkdir artifacts

View File

@@ -8,6 +8,10 @@ on:
- 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
permissions:
id-token: write
contents: write
# on: # on:
# push: # push:
# branches: # branches:
@@ -43,13 +47,17 @@ jobs:
cd dbgate-merged cd dbgate-merged
node adjustNpmPackageJsonPremium node adjustNpmPackageJsonPremium
- name: Configure NPM token # - name: Configure NPM token
env: # env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: | # run: |
cd .. # cd ..
cd dbgate-merged # cd dbgate-merged
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" # npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: Remove dbmodel - should be not published - name: Remove dbmodel - should be not published
run: | run: |
@@ -69,33 +77,40 @@ jobs:
cd dbgate-merged cd dbgate-merged
yarn setCurrentVersion yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish dbgate-api-premium - name: Publish dbgate-api-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/api cd dbgate-merged/packages/api
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-web-premium - name: Publish dbgate-web-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/web cd dbgate-merged/packages/web
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve-premium - name: Publish dbgate-serve-premium
run: | run: |
cd .. cd ..
cd dbgate-merged/packages/serve cd dbgate-merged/packages/serve
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cosmosdb - name: Publish dbgate-plugin-cosmosdb
run: | run: |
cd .. cd ..
cd dbgate-merged/plugins/dbgate-plugin-cosmosdb cd dbgate-merged/plugins/dbgate-plugin-cosmosdb
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-firestore - name: Publish dbgate-plugin-firestore
run: | run: |
cd .. cd ..
cd dbgate-merged/plugins/dbgate-plugin-firestore cd dbgate-merged/plugins/dbgate-plugin-firestore
npm publish npm publish --tag "$NPM_TAG"

View File

@@ -8,6 +8,10 @@ on:
- 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
permissions:
id-token: write
contents: write
# on: # on:
# push: # push:
# branches: # branches:
@@ -35,11 +39,15 @@ jobs:
with: with:
node-version: 22.x node-version: 22.x
- name: Configure NPM token # Ensure npm 11.5.1 or later is installed
env: - name: Update npm
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npm install -g npm@latest
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" # - name: Configure NPM token
# env:
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: |
# npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: yarn install - name: yarn install
run: | run: |
@@ -49,112 +57,120 @@ jobs:
run: | run: |
yarn setCurrentVersion yarn setCurrentVersion
- name: Compute npm dist-tag
run: |
if [[ "${GITHUB_REF_NAME}" =~ -alpha\. ]]; then
echo "NPM_TAG=alpha" >> $GITHUB_ENV
else
echo "NPM_TAG=latest" >> $GITHUB_ENV
fi
- name: Publish types - name: Publish types
working-directory: packages/types working-directory: packages/types
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish tools - name: Publish tools
working-directory: packages/tools working-directory: packages/tools
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish sqltree - name: Publish sqltree
working-directory: packages/sqltree working-directory: packages/sqltree
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish api - name: Publish api
working-directory: packages/api working-directory: packages/api
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish datalib - name: Publish datalib
working-directory: packages/datalib working-directory: packages/datalib
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish filterparser - name: Publish filterparser
working-directory: packages/filterparser working-directory: packages/filterparser
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish web - name: Publish web
working-directory: packages/web working-directory: packages/web
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-serve - name: Publish dbgate-serve
working-directory: packages/serve working-directory: packages/serve
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbmodel - name: Publish dbmodel
working-directory: packages/dbmodel working-directory: packages/dbmodel
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-csv - name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv working-directory: plugins/dbgate-plugin-csv
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-xml - name: Publish dbgate-plugin-xml
working-directory: plugins/dbgate-plugin-xml working-directory: plugins/dbgate-plugin-xml
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-excel - name: Publish dbgate-plugin-excel
working-directory: plugins/dbgate-plugin-excel working-directory: plugins/dbgate-plugin-excel
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mssql - name: Publish dbgate-plugin-mssql
working-directory: plugins/dbgate-plugin-mssql working-directory: plugins/dbgate-plugin-mssql
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mysql - name: Publish dbgate-plugin-mysql
working-directory: plugins/dbgate-plugin-mysql working-directory: plugins/dbgate-plugin-mysql
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-mongo - name: Publish dbgate-plugin-mongo
working-directory: plugins/dbgate-plugin-mongo working-directory: plugins/dbgate-plugin-mongo
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-postgres - name: Publish dbgate-plugin-postgres
working-directory: plugins/dbgate-plugin-postgres working-directory: plugins/dbgate-plugin-postgres
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-sqlite - name: Publish dbgate-plugin-sqlite
working-directory: plugins/dbgate-plugin-sqlite working-directory: plugins/dbgate-plugin-sqlite
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-redis - name: Publish dbgate-plugin-redis
working-directory: plugins/dbgate-plugin-redis working-directory: plugins/dbgate-plugin-redis
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-oracle - name: Publish dbgate-plugin-oracle
working-directory: plugins/dbgate-plugin-oracle working-directory: plugins/dbgate-plugin-oracle
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-clickhouse - name: Publish dbgate-plugin-clickhouse
working-directory: plugins/dbgate-plugin-clickhouse working-directory: plugins/dbgate-plugin-clickhouse
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-dbf - name: Publish dbgate-plugin-dbf
working-directory: plugins/dbgate-plugin-dbf working-directory: plugins/dbgate-plugin-dbf
run: | run: |
npm publish npm publish --tag "$NPM_TAG"
- name: Publish dbgate-plugin-cassandra - name: Publish dbgate-plugin-cassandra
working-directory: plugins/dbgate-plugin-cassandra working-directory: plugins/dbgate-plugin-cassandra
run: | run: |
npm publish npm publish --tag "$NPM_TAG"

View File

@@ -7,7 +7,7 @@ checkout-and-merge-pro:
repository: dbgate/dbgate-pro repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro path: dbgate-pro
ref: 11203754aad94189b565c2816d37760b15c8e07f ref: 6195e103e1d45e4f59bade60df5dd1784f4e6c77
- name: Merge dbgate/dbgate-pro - name: Merge dbgate/dbgate-pro
run: | run: |
mkdir ../dbgate-pro mkdir ../dbgate-pro