mirror of
https://github.com/beekeeper-studio/beekeeper-studio.git
synced 2026-03-13 10:12:54 +08:00
Merge branch 'rc-56'
This commit is contained in:
8
.github/workflows/studio-publish.yml
vendored
8
.github/workflows/studio-publish.yml
vendored
@@ -105,8 +105,12 @@ jobs:
|
||||
# Why does it fill up zfs? I don't understand what it does at all
|
||||
- name: Free Up ZFS Space
|
||||
run: |
|
||||
sudo zfs list -H -o name -t snapshot | grep "snapcraft" | xargs -I{} sudo zfs destroy {}
|
||||
sudo zfs list -H -o name | grep "snapcraft" | xargs -I{} sudo zfs destroy {}
|
||||
# Force unmount any snapcraft-related ZFS datasets that are busy
|
||||
sudo zfs list -H -o name | grep "snapcraft" | sort -r | xargs -I{} sudo zfs unmount -f {} 2>/dev/null || true
|
||||
# Destroy datasets (clones) first, deepest children first
|
||||
sudo zfs list -H -o name | grep "snapcraft" | sort -r | xargs -I{} sudo zfs destroy -rR {} 2>/dev/null || true
|
||||
# Then destroy any remaining snapshots
|
||||
sudo zfs list -H -o name -t snapshot | grep "snapcraft" | xargs -I{} sudo zfs destroy -rR {} 2>/dev/null || true
|
||||
if: matrix.os.clean_zfs
|
||||
|
||||
- name: Install flatpak tools
|
||||
|
||||
34
.github/workflows/ui-kit-publish.yml
vendored
Normal file
34
.github/workflows/ui-kit-publish.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: UI Kit - Build & Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "ui-kit:v*"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: yarn install
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
|
||||
- name: Run tests
|
||||
run: yarn workspace @beekeeperstudio/ui-kit run test
|
||||
|
||||
- name: Build
|
||||
run: yarn workspace @beekeeperstudio/ui-kit run build
|
||||
|
||||
- name: Publish to npm
|
||||
run: yarn workspace @beekeeperstudio/ui-kit npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "beekeeper-studio",
|
||||
"version": "5.6.0-beta.3",
|
||||
"version": "5.6.0-beta.8",
|
||||
"private": true,
|
||||
"description": "An easy-to use SQL query editor and database UI for Mac, Windows, and Linux",
|
||||
"author": {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<workspace-rename-modal />
|
||||
<import-queries-modal />
|
||||
<import-connections-modal />
|
||||
<plugin-controller />
|
||||
<plugin-controller :editor-font-size="editorFontSize" />
|
||||
<plugin-manager-modal />
|
||||
<keyboard-shortcuts-modal />
|
||||
<confirmation-modal-manager />
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div v-show="azureAuthEnabled" class="host-port-user-password">
|
||||
<common-ssl
|
||||
:config="config"
|
||||
:ssl-help="sslHelp"
|
||||
:support-complex-s-s-l="supportComplexSSL"
|
||||
/>
|
||||
<div class="alert alert-info">
|
||||
<i class="material-icons-outlined">info</i>
|
||||
<div v-if="showCli">
|
||||
@@ -23,12 +28,7 @@
|
||||
Azure CLI Path (az)
|
||||
</label
|
||||
>
|
||||
<input
|
||||
name="cliPath"
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="config.azureAuthOptions.cliPath"
|
||||
/>
|
||||
<file-picker v-model="config.azureAuthOptions.cliPath"/>
|
||||
<div class="alert alert-danger" v-show="!cliFound">
|
||||
<i class="material-icons-outlined">warning</i>
|
||||
<div>
|
||||
@@ -49,7 +49,7 @@
|
||||
style="padding-left: 0.25rem"
|
||||
v-tooltip="{
|
||||
content:
|
||||
'This is the <code>\'Server name\'</code> field on your Sql Server in Azure, <br/> you might also think of this as the hostname. <br/> Eg. <code>example.database.windows.net</code>',
|
||||
'This is the <code>\'Server name\'</code> field on your database in Azure, <br/> you might also think of this as the hostname. <br/> Eg. <code>example.database.windows.net</code>',
|
||||
html: true,
|
||||
}"
|
||||
>help_outlined</i
|
||||
@@ -85,6 +85,15 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="showUser">
|
||||
<label for="user">User</label>
|
||||
<input
|
||||
name="user"
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="config.username"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group" v-show="isServicePrincipal">
|
||||
<label for="tenantId">
|
||||
Tenant ID
|
||||
@@ -131,12 +140,24 @@ import { AzureAuthType } from "@/lib/db/types";
|
||||
import { AppEvent } from "@/common/AppEvent";
|
||||
import _ from "lodash";
|
||||
import MaskedInput from '@/components/MaskedInput.vue'
|
||||
import CommonSsl from './CommonSsl.vue'
|
||||
import { mapState } from 'vuex'
|
||||
import FilePicker from '@/components/common/form/FilePicker.vue'
|
||||
|
||||
export default {
|
||||
props: ["config", "authType"],
|
||||
props: {
|
||||
config: Object,
|
||||
authType: [String, Number],
|
||||
sslHelp: String,
|
||||
supportComplexSSL: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
MaskedInput,
|
||||
CommonSsl,
|
||||
FilePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -215,14 +236,14 @@ export default {
|
||||
try {
|
||||
const result = await this.$util.send('backup/whichDumpTool', {toolName: "az"});
|
||||
if (result) {
|
||||
this.config.azureAuthOptions.cliPath = result;
|
||||
this.$set(this.config.azureAuthOptions, 'cliPath', result);
|
||||
this.cliError = false;
|
||||
} else {
|
||||
this.config.azureAuthOptions.cliPath = null;
|
||||
this.$set(this.config.azureAuthOptions, 'cliPath', null);
|
||||
this.cliError = true;
|
||||
}
|
||||
} catch (e) {
|
||||
this.config.azureAuthOptions.cliPath = null;
|
||||
this.$set(this.config.azureAuthOptions, 'cliPath', null);
|
||||
this.cliError = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
name="cliPath"
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="cliPath"
|
||||
/>
|
||||
<file-picker v-model="cliPath"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -125,12 +120,14 @@
|
||||
|
||||
<script>
|
||||
import MaskedInput from '@/components/MaskedInput.vue'
|
||||
import FilePicker from '@/components/common/form/FilePicker.vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
props: ['config', 'authType'],
|
||||
components: {
|
||||
MaskedInput,
|
||||
FilePicker
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -68,102 +68,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<toggle-form-area
|
||||
title="Enable SSL"
|
||||
v-if="supportComplexSSL && supportsSsl"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<x-switch
|
||||
@click.prevent="toggleSsl"
|
||||
:toggled="config.ssl"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:default>
|
||||
<div class="row gutter">
|
||||
<div class="alert alert-info">
|
||||
<i class="material-icons-outlined">info</i>
|
||||
<div>
|
||||
Providing certificate files is optional. By default Beekeeper will just trust the server certificate.
|
||||
<external-link href="https://docs.beekeeperstudio.io/user_guide/connecting/connecting/#ssl">
|
||||
Read More
|
||||
</external-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>CA Cert (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslCaFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>Certificate (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslCertFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>Key File (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslKeyFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label
|
||||
class="checkbox-group"
|
||||
for="reject"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="reject"
|
||||
type="checkbox"
|
||||
name="rememberPassword"
|
||||
v-model="config.sslRejectUnauthorized"
|
||||
>
|
||||
<span>Reject Unauthorized</span>
|
||||
<i
|
||||
class="material-icons"
|
||||
v-tooltip="'This only takes effect if you provide certificate files'"
|
||||
>help_outlined</i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</toggle-form-area>
|
||||
|
||||
|
||||
<!-- Simple SSL -->
|
||||
<div
|
||||
v-else-if="supportsSsl"
|
||||
class="advanced-connection-settings"
|
||||
>
|
||||
<div class="flex flex-middle">
|
||||
<h4
|
||||
class="advanced-heading flex"
|
||||
:class="{enabled: config.ssl}"
|
||||
>
|
||||
<span class="expand">Enable SSL</span>
|
||||
<x-switch
|
||||
@click.prevent="toggleSsl"
|
||||
:toggled="config.ssl"
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<small class="text-muted help">{{ sslHelp }}</small>
|
||||
</div>
|
||||
<common-ssl
|
||||
:config="config"
|
||||
:ssl-help="sslHelp"
|
||||
:supportComplexSSL="supportComplexSSL"
|
||||
/>
|
||||
|
||||
<div class="row gutter">
|
||||
<div class="col form-group" :class="[showPasswordForm ? 's6' : 's12']">
|
||||
@@ -207,11 +116,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FilePicker from '@/components/common/form/FilePicker.vue'
|
||||
import ExternalLink from '@/components/common/ExternalLink.vue'
|
||||
import { findClient } from '@/lib/db/clients'
|
||||
import ToggleFormArea from '../common/ToggleFormArea.vue'
|
||||
import MaskedInput from '@/components/MaskedInput.vue'
|
||||
import CommonSsl from './CommonSsl.vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
@@ -228,34 +135,22 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FilePicker,
|
||||
ExternalLink,
|
||||
ToggleFormArea,
|
||||
MaskedInput
|
||||
MaskedInput,
|
||||
CommonSsl
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sslToggled: false,
|
||||
showPassword: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('settings', ['privacyMode']),
|
||||
hasAdvancedSsl() {
|
||||
return this.config.sslCaFile || this.config.sslCertFile || this.config.sslKeyFile
|
||||
},
|
||||
toggleIcon() {
|
||||
return this.sslToggled ? 'keyboard_arrow_down' : 'keyboard_arrow_right'
|
||||
},
|
||||
togglePasswordIcon() {
|
||||
return this.showPassword ? "visibility_off" : "visibility"
|
||||
},
|
||||
togglePasswordInputType() {
|
||||
return this.showPassword ? "text" : "password"
|
||||
},
|
||||
supportsSsl() {
|
||||
return findClient(this.config.connectionType).supports('server:ssl')
|
||||
},
|
||||
supportsSocketPath() {
|
||||
return findClient(this.config.connectionType).supportsSocketPath
|
||||
},
|
||||
@@ -276,25 +171,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
},
|
||||
toggleSsl() {
|
||||
this.config.ssl = !this.config.ssl
|
||||
|
||||
// Remove CA file when disabling ssl
|
||||
if (!this.config.ssl) {
|
||||
this.config.sslCaFile = null
|
||||
this.config.sslCertFile = null
|
||||
this.config.sslKeyFile = null
|
||||
}
|
||||
},
|
||||
toggleSslAdvanced() {
|
||||
this.sslToggled = !this.sslToggled;
|
||||
},
|
||||
togglePassword() {
|
||||
this.showPassword = !this.showPassword
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.sslToggled = this.hasAdvancedSsl
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
158
apps/studio/src/components/connection/CommonSsl.vue
Normal file
158
apps/studio/src/components/connection/CommonSsl.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div>
|
||||
<toggle-form-area
|
||||
title="Enable SSL"
|
||||
v-if="supportComplexSSL && supportsSsl"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<x-switch
|
||||
@click.prevent="toggleSsl"
|
||||
:toggled="config.ssl"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:default>
|
||||
<div class="row gutter">
|
||||
<div class="alert alert-info">
|
||||
<i class="material-icons-outlined">info</i>
|
||||
<div>
|
||||
Providing certificate files is optional. By default Beekeeper will just trust the server certificate.
|
||||
<external-link href="https://docs.beekeeperstudio.io/user_guide/connecting/connecting/#ssl">
|
||||
Read More
|
||||
</external-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>CA Cert (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslCaFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>Certificate (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslCertFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label>Key File (optional)</label>
|
||||
<file-picker
|
||||
v-model="config.sslKeyFile"
|
||||
:disabled="!config.ssl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gutter">
|
||||
<div class="col form-group">
|
||||
<label
|
||||
class="checkbox-group"
|
||||
for="reject"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="reject"
|
||||
type="checkbox"
|
||||
name="rememberPassword"
|
||||
v-model="config.sslRejectUnauthorized"
|
||||
>
|
||||
<span>Reject Unauthorized</span>
|
||||
<i
|
||||
class="material-icons"
|
||||
v-tooltip="'This only takes effect if you provide certificate files'"
|
||||
>help_outlined</i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</toggle-form-area>
|
||||
|
||||
<!-- Simple SSL -->
|
||||
<div
|
||||
v-else-if="supportsSsl"
|
||||
class="advanced-connection-settings"
|
||||
>
|
||||
<div class="flex flex-middle">
|
||||
<h4
|
||||
class="advanced-heading flex"
|
||||
:class="{enabled: config.ssl}"
|
||||
>
|
||||
<span class="expand">Enable SSL</span>
|
||||
<x-switch
|
||||
@click.prevent="toggleSsl"
|
||||
:toggled="config.ssl"
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<small class="text-muted help">{{ sslHelp }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import FilePicker from '@/components/common/form/FilePicker.vue'
|
||||
import ExternalLink from '@/components/common/ExternalLink.vue'
|
||||
import ToggleFormArea from '../common/ToggleFormArea.vue'
|
||||
import { findClient } from '@/lib/db/clients'
|
||||
import { PropType } from 'vue'
|
||||
import { IConnection } from '@/common/interfaces/IConnection'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
config: Object as PropType<IConnection>,
|
||||
sslHelp: String,
|
||||
supportComplexSSL: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FilePicker,
|
||||
ExternalLink,
|
||||
ToggleFormArea
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sslToggled: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasAdvancedSsl() {
|
||||
return this.config.sslCaFile || this.config.sslCertFile || this.config.sslKeyFile
|
||||
},
|
||||
toggleIcon() {
|
||||
return this.sslToggled ? 'keyboard_arrow_down' : 'keyboard_arrow_right'
|
||||
},
|
||||
supportsSsl() {
|
||||
return findClient(this.config.connectionType).supports('server:ssl')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSsl() {
|
||||
this.config.ssl = !this.config.ssl
|
||||
|
||||
// Remove CA file when disabling ssl
|
||||
if (!this.config.ssl) {
|
||||
this.config.sslCaFile = null
|
||||
this.config.sslCertFile = null
|
||||
this.config.sslKeyFile = null
|
||||
}
|
||||
},
|
||||
toggleSslAdvanced() {
|
||||
this.sslToggled = !this.sslToggled
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.sslToggled = this.hasAdvancedSsl
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -12,8 +12,8 @@
|
||||
<common-server-inputs v-show="showServerInputs" :config="config" :show-password-form="showPasswordForm" />
|
||||
|
||||
<common-iam v-show="iamAuthenticationEnabled" :auth-type="authType" :config="config" />
|
||||
<common-advanced :config="config" />
|
||||
<common-entra-id v-show="azureAuthEnabled" :auth-type="authType" :config="config" />
|
||||
<common-advanced :config="config" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,6 +33,9 @@ export default {
|
||||
props: ['config'],
|
||||
mounted() {
|
||||
this.azureAuthEnabled = this.config?.azureAuthOptions?.azureAuthEnabled || false
|
||||
if (this.authType !== 'default') {
|
||||
this.showPasswordForm = false;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -40,7 +43,6 @@ export default {
|
||||
iamAuthenticationEnabled: !!this.config.iamAuthOptions?.iamAuthenticationEnabled,
|
||||
authType: this.config.iamAuthOptions?.authType || this.config.azureAuthOptions?.azureAuthType || 'default',
|
||||
authTypes: [{ name: 'Username / Password', value: 'default' }, ...IamAuthTypes, ...AzureAuthTypes.filter(auth => auth.value === AzureAuthType.CLI)],
|
||||
accountName: null,
|
||||
signingOut: false,
|
||||
errorSigningOut: null,
|
||||
showPasswordForm: true
|
||||
@@ -67,20 +69,16 @@ export default {
|
||||
this.showPasswordForm = false;
|
||||
if (typeof this.authType === 'string' && this.authType.includes('iam')) {
|
||||
this.iamAuthenticationEnabled = true;
|
||||
this.azureAuthEnabled = false;
|
||||
this.config.iamAuthOptions.authType = this.authType;
|
||||
} else if (this.authType === AzureAuthType.CLI) {
|
||||
this.azureAuthEnabled = true;
|
||||
this.iamAuthenticationEnabled = false;
|
||||
this.config.azureAuthOptions.azureAuthType = this.authType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const authId = this.config.azureAuthOptions?.authId || this.config?.authId
|
||||
if (this.authType === AzureAuthType.CLI && !_.isNil(authId)) {
|
||||
this.accountName = await this.connection.azureGetAccountName(authId);
|
||||
} else {
|
||||
this.accountName = null
|
||||
}
|
||||
},
|
||||
config() {
|
||||
if (this.config.azureAuthOptions.azureAuthEnabled) {
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<input type="text" class="form-control" v-model="config.options.cluster">
|
||||
</div>
|
||||
<common-iam v-show="iamAuthenticationEnabled" :auth-type="authType" :config="config" />
|
||||
<common-advanced :config="config" />
|
||||
<common-entra-id v-show="azureAuthEnabled" :auth-type="authType" :config="config" />
|
||||
<common-advanced :config="config" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -52,7 +52,6 @@ export default {
|
||||
iamAuthenticationEnabled: !!this.config.iamAuthOptions?.iamAuthenticationEnabled,
|
||||
authType: this.config.iamAuthOptions?.authType || this.config.azureAuthOptions?.azureAuthType || 'default',
|
||||
authTypes: [{ name: 'Username / Password', value: 'default' }, ...IamAuthTypes, ...AzureAuthTypes.filter(auth => auth.value === AzureAuthType.CLI)],
|
||||
accountName: null,
|
||||
signingOut: false,
|
||||
errorSigningOut: null,
|
||||
showPasswordForm: true
|
||||
@@ -88,13 +87,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const authId = this.config.azureAuthOptions?.authId || this.config?.authId
|
||||
if (this.authType === AzureAuthType.AccessToken && !_.isNil(authId)) {
|
||||
this.accountName = await this.connection.azureGetAccountName(authId);
|
||||
} else {
|
||||
this.accountName = null
|
||||
}
|
||||
},
|
||||
config() {
|
||||
if (this.config.azureAuthOptions.azureAuthEnabled) {
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</common-server-inputs>
|
||||
<common-advanced v-show="!azureAuthEnabled" :config="config" />
|
||||
<common-entra-id v-show="azureAuthEnabled" :auth-type="authType" :config="config" />
|
||||
<common-advanced v-show="!azureAuthEnabled" :config="config" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
azureAuthEnabled: false,
|
||||
authType: 'default',
|
||||
authTypes: AzureAuthTypes,
|
||||
accountName: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -68,6 +68,7 @@ export default Vue.extend({
|
||||
await this.$nextTick();
|
||||
if (this.shouldMountIframe) {
|
||||
this.mountIframe().catch((e) => {
|
||||
log.error(e);
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,9 @@ import { AppEvent } from "@/common/AppEvent";
|
||||
import { NativePluginMenuItem } from "@/services/plugin";
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
editorFontSize: Number,
|
||||
},
|
||||
computed: {
|
||||
rootBindings() {
|
||||
return [
|
||||
@@ -19,6 +22,14 @@ export default Vue.extend({
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editorFontSize() {
|
||||
this.$plugin.notifyAll({
|
||||
name: "editorFontSizeChanged",
|
||||
args: { value: this.editorFontSize },
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChangedTheme(themeValue: string) {
|
||||
const data: PluginNotificationData = {
|
||||
|
||||
@@ -111,11 +111,15 @@ export default Vue.extend({
|
||||
if (item.expanded) {
|
||||
expandedMap.set(item.key, true);
|
||||
}
|
||||
if (item.pinned) {
|
||||
pinnedMap.set(item.key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const pin of this.pins) {
|
||||
pinnedMap.set(pin.entity, pin);
|
||||
const key = entityId(pin.schemaName, pin.entity);
|
||||
pinnedMap.set(key, true);
|
||||
}
|
||||
|
||||
this.schemaTables.forEach((schema: any) => {
|
||||
@@ -153,7 +157,7 @@ export default Vue.extend({
|
||||
contextMenu: this.tableMenuOptions,
|
||||
parent,
|
||||
level: noFolder ? 0 : 1,
|
||||
pinned: pinnedMap.get(table) || false,
|
||||
pinned: pinnedMap.has(key) || false,
|
||||
loadingColumns: false,
|
||||
});
|
||||
});
|
||||
@@ -169,7 +173,7 @@ export default Vue.extend({
|
||||
contextMenu: this.routineMenuOptions,
|
||||
parent,
|
||||
level: noFolder ? 0 : 1,
|
||||
pinned: pinnedMap.get(routine) || false,
|
||||
pinned: pinnedMap.has(key) || false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -272,13 +276,12 @@ export default Vue.extend({
|
||||
},
|
||||
handleTogglePinned(entity: Entity, pinned?: boolean) {
|
||||
const item = this.items.find((item: Item) => item.entity === entity);
|
||||
if (!item) return;
|
||||
|
||||
if (typeof pinned === "undefined") {
|
||||
pinned = !item.pinned;
|
||||
}
|
||||
item.pinned = !item.pinned;
|
||||
|
||||
if (pinned) this.$store.dispatch('pins/add', entity)
|
||||
else this.$store.dispatch('pins/remove', entity)
|
||||
item.pinned = pinned;
|
||||
},
|
||||
handleScrollEnd() {
|
||||
this.updateTableColumnsInRange(true);
|
||||
|
||||
@@ -35,6 +35,7 @@ const camelCaseData: AxiosResponseTransformer = (data) => {
|
||||
export interface CloudClientOptions {
|
||||
token: string,
|
||||
app: string,
|
||||
clientVersion: string,
|
||||
email: string
|
||||
baseUrl: string,
|
||||
workspace?: number
|
||||
@@ -85,7 +86,8 @@ export class CloudClient {
|
||||
headers: {
|
||||
email: options.email,
|
||||
token: options.token,
|
||||
app: options.app
|
||||
app: options.app,
|
||||
clientVersion: options.clientVersion,
|
||||
},
|
||||
validateStatus: (status) => status < 500
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ import {TokenCache} from '@/common/appdb/models/token_cache';
|
||||
import globals from '@/common/globals';
|
||||
import {AzureAuthOptions, AzureAuthType} from '../types';
|
||||
import {spawn} from 'child_process'
|
||||
import {getEntraOptions} from "@/lib/db/clients/utils";
|
||||
import {getEntraOptions, sanitizeCommandPath} from "@/lib/db/clients/utils";
|
||||
import {IDbConnectionServer} from "@/lib/db/backendTypes";
|
||||
import BksConfig from '@/common/bksConfig';
|
||||
|
||||
@@ -178,14 +178,14 @@ export class AzureAuthService {
|
||||
}
|
||||
|
||||
return new Promise<AuthConfig>((resolve, reject) => {
|
||||
const proc = spawn(options.cliPath, [
|
||||
const proc = spawn(sanitizeCommandPath(options.cliPath), [
|
||||
'account',
|
||||
'get-access-token',
|
||||
'--resource',
|
||||
BksConfig.azure.azSQLLoginScope,
|
||||
'--output',
|
||||
'json'
|
||||
]);
|
||||
], { shell: true });
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
@@ -6,7 +6,6 @@ import { joinFilters } from '@/common/utils'
|
||||
import { IdentifyResult } from 'sql-query-identifier/lib/defines'
|
||||
import { fromIni } from "@aws-sdk/credential-providers";
|
||||
import { Signer } from "@aws-sdk/rds-signer";
|
||||
import globals from "@/common/globals";
|
||||
import {
|
||||
AWSCredentials
|
||||
} from "@/lib/db/authentication/amazon-redshift";
|
||||
@@ -15,6 +14,7 @@ import { AuthOptions } from "@/lib/db/authentication/azure";
|
||||
import { spawn } from "child_process";
|
||||
import { loadSharedConfigFiles } from "@aws-sdk/shared-ini-file-loader";
|
||||
import { AwsCredentialIdentity, RuntimeConfigAwsCredentialIdentityProvider } from '@aws-sdk/types'
|
||||
import platformInfo from '@/common/platform_info'
|
||||
|
||||
const log = logRaw.scope('db/util')
|
||||
|
||||
@@ -437,6 +437,18 @@ export async function refreshTokenIfNeeded(iamOptions: IamAuthOptions, server: a
|
||||
return resolvedPw;
|
||||
}
|
||||
|
||||
export function sanitizeCommandPath(path: string): string {
|
||||
if (!path) return path;
|
||||
|
||||
if (platformInfo.isWindows) {
|
||||
const escaped = path.replace(/"/g, '""');
|
||||
return `"${escaped}"`;
|
||||
} else {
|
||||
const escaped = path.replace(/'/g, "'\\''");
|
||||
return `'${escaped}'`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAWSCLIToken(server: IDbConnectionServerConfig, options: IamAuthOptions): Promise<string> {
|
||||
if (!options?.cliPath) {
|
||||
throw new Error('AZ command not specified');
|
||||
@@ -449,7 +461,7 @@ export async function getAWSCLIToken(server: IDbConnectionServerConfig, options:
|
||||
}
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const proc = spawn(options.cliPath, [
|
||||
const proc = spawn(sanitizeCommandPath(options.cliPath), [
|
||||
'rds',
|
||||
'generate-db-auth-token',
|
||||
'--hostname',
|
||||
@@ -461,7 +473,7 @@ export async function getAWSCLIToken(server: IDbConnectionServerConfig, options:
|
||||
'--username',
|
||||
server.user,
|
||||
...extraArgs
|
||||
]);
|
||||
], { shell: true });
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
@@ -59,7 +59,7 @@ export default class PluginStoreService {
|
||||
}
|
||||
|
||||
getTheme() {
|
||||
const styles = getComputedStyle(document.body);
|
||||
const styles = getComputedStyle(this.getAppEl());
|
||||
/** Key = css property, value = css value */
|
||||
const palette: Record<string, string> = {};
|
||||
|
||||
@@ -384,4 +384,8 @@ export default class PluginStoreService {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private getAppEl() {
|
||||
return document.body.querySelector('.beekeeper-studio-wrapper');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export const cssVars = [
|
||||
"--bks-text-editor-error-fg-color",
|
||||
"--bks-text-editor-fg-color",
|
||||
"--bks-text-editor-focused-outline-color",
|
||||
"--bks-text-editor-font-size",
|
||||
"--bks-text-editor-foldgutter-fg-color",
|
||||
"--bks-text-editor-foldgutter-fg-color-hover",
|
||||
"--bks-text-editor-gutter-bg-color",
|
||||
|
||||
@@ -31,7 +31,7 @@ interface State {
|
||||
|
||||
async function credentialToBlob(c: TransportCloudCredential): Promise<CredentialBlob> {
|
||||
const clientOptions: CloudClientOptions = {
|
||||
app: c.appId, email: c.email, token: c.token, baseUrl: window.platformInfo.cloudUrl
|
||||
app: c.appId, email: c.email, token: c.token, baseUrl: window.platformInfo.cloudUrl, clientVersion: window.platformInfo.appVersion
|
||||
}
|
||||
const client = new CloudClient(clientOptions)
|
||||
try {
|
||||
|
||||
@@ -62,9 +62,9 @@ export const PinConnectionModule: Module<State, RootState> = {
|
||||
return;
|
||||
}
|
||||
|
||||
const newPin = await Vue.prototype.$util.send('appdb/pinconn/new', { init: item });
|
||||
let newPin = await Vue.prototype.$util.send('appdb/pinconn/new', { init: item });
|
||||
newPin.position = (context.getters.orderedPins.reverse()[0]?.position || 0) + 1;
|
||||
await Vue.prototype.$util.send('appdb/pinconn/save', { obj: newPin })
|
||||
newPin = await Vue.prototype.$util.send('appdb/pinconn/save', { obj: newPin })
|
||||
context.commit('add', newPin);
|
||||
},
|
||||
async reorder(context) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import Vue from "vue";
|
||||
|
||||
function matches(pin: TransportPinnedEntity, entity: DatabaseEntity, database?: string) {
|
||||
return entity.name === pin.entityName &&
|
||||
((_.isNil(entity.schema) && _.isNil(pin.schemaName)) ||
|
||||
((_.isNil(entity.schema) && _.isNil(pin.schemaName)) ||
|
||||
entity.schema === pin.schemaName) &&
|
||||
entity.entityType === pin.entityType &&
|
||||
(!database || database === pin.databaseName)
|
||||
@@ -75,7 +75,7 @@ export const PinModule: Module<State, RootState> = {
|
||||
// this used to be !p.hasId(), hopefully this still works? the alternative is ugly
|
||||
const unsavedPins = context.state.pins.filter((p)=> !p.id)
|
||||
await Promise.all(unsavedPins.map((p) => {
|
||||
p.connectionId === usedConfig.id && p.workspaceId === usedConfig.id &&
|
||||
p.connectionId === usedConfig.id && p.workspaceId === usedConfig.id &&
|
||||
Vue.prototype.$util.send('appdb/pins/save', { obj: p });
|
||||
}))
|
||||
},
|
||||
@@ -86,7 +86,7 @@ export const PinModule: Module<State, RootState> = {
|
||||
|
||||
if (database && usedConfig) {
|
||||
console.log('GETTING NEW PIN: ', item, database, usedConfig)
|
||||
const newPin = await Vue.prototype.$util.send('appdb/pins/new', {
|
||||
let newPin = await Vue.prototype.$util.send('appdb/pins/new', {
|
||||
init: {
|
||||
table: item,
|
||||
db: database,
|
||||
@@ -95,7 +95,9 @@ export const PinModule: Module<State, RootState> = {
|
||||
});
|
||||
console.log('RECEIVED NEW PIN: ', newPin)
|
||||
newPin.position = (context.getters.orderedPins.reverse()[0]?.position || 0) + 1
|
||||
if(usedConfig.id) await Vue.prototype.$util.send('appdb/pins/save', { obj: newPin });
|
||||
if(usedConfig.id) {
|
||||
newPin = await Vue.prototype.$util.send('appdb/pins/save', { obj: newPin });
|
||||
}
|
||||
context.commit('add', newPin)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"url": "https://beekeeperstudio.io"
|
||||
},
|
||||
"private": false,
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/beekeeper-studio/beekeeper-studio.git",
|
||||
|
||||
114
bin/make-ui-kit-release.sh
Executable file
114
bin/make-ui-kit-release.sh
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TAG_PREFIX="ui-kit:v"
|
||||
|
||||
# Function to list the 5 most recent remote ui-kit tags by date
|
||||
list_recent_remote_tags() {
|
||||
echo "Fetching the 5 most recent remote ui-kit tags by date:"
|
||||
git ls-remote --tags origin | \
|
||||
grep -E "refs/tags/${TAG_PREFIX}[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?$" | \
|
||||
while read -r hash ref; do
|
||||
tag="${ref#refs/tags/}"
|
||||
date=$(git log -1 --format='%ci' "$hash" 2>/dev/null || echo "unknown date")
|
||||
echo "$tag $date"
|
||||
done | sort -k2 -r | head -n 5
|
||||
}
|
||||
|
||||
# Function to get the next version in sequence
|
||||
guess_next_version() {
|
||||
# Get the most recent ui-kit tag
|
||||
local latest_tag
|
||||
latest_tag=$(git ls-remote --tags origin | \
|
||||
grep -Eo "${TAG_PREFIX}[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?$" | \
|
||||
sed "s/^${TAG_PREFIX}//" | \
|
||||
sort -V | tail -n 1)
|
||||
|
||||
if [[ -z "$latest_tag" ]]; then
|
||||
echo "0.1.0"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$latest_tag" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-(alpha|beta)\.([0-9]+))?$ ]]; then
|
||||
major="${BASH_REMATCH[1]}"
|
||||
minor="${BASH_REMATCH[2]}"
|
||||
patch="${BASH_REMATCH[3]}"
|
||||
channel="${BASH_REMATCH[5]:-}"
|
||||
channel_num="${BASH_REMATCH[6]:-}"
|
||||
|
||||
if [[ -n "$channel" ]]; then
|
||||
# Increment the channel number for pre-releases
|
||||
channel_num=$((channel_num + 1))
|
||||
echo "$major.$minor.$patch-$channel.$channel_num"
|
||||
else
|
||||
# Increment the patch version for stable releases
|
||||
patch=$((patch + 1))
|
||||
echo "$major.$minor.$patch"
|
||||
fi
|
||||
else
|
||||
echo "0.1.0" # Default to this if no valid tags exist
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to validate version format
|
||||
validate_version() {
|
||||
if [[ "$1" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)$ ]]; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
else
|
||||
echo "Error: Invalid version format. Expected x.x.x or x.x.x-channel.x"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Step 1: List recent tags
|
||||
list_recent_remote_tags
|
||||
|
||||
# Step 2: Prompt user for new version with a default guess
|
||||
default_version=$(guess_next_version)
|
||||
echo ""
|
||||
read -p "Enter the new version (default: $default_version): " INPUT_VERSION
|
||||
VERSION="${INPUT_VERSION:-$default_version}"
|
||||
|
||||
# Step 3: Validate and clean the version
|
||||
VERSION=$(validate_version "$VERSION")
|
||||
NEW_TAG="${TAG_PREFIX}${VERSION}"
|
||||
|
||||
# Confirm with the user
|
||||
echo ""
|
||||
echo "This will:"
|
||||
echo " - Update apps/ui-kit/package.json to version: $VERSION"
|
||||
echo " - Push a new git tag: $NEW_TAG"
|
||||
read -p "Do you want to continue? (y/n): " CONFIRM
|
||||
|
||||
if [[ "$CONFIRM" != "y" ]]; then
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 4: Update package.json if necessary
|
||||
PACKAGE_VERSION=$(jq -r '.version' apps/ui-kit/package.json)
|
||||
if [[ "$PACKAGE_VERSION" == "$VERSION" ]]; then
|
||||
echo "package.json is already updated to version $VERSION."
|
||||
else
|
||||
echo "Updating apps/ui-kit/package.json..."
|
||||
jq ".version = \"$VERSION\"" apps/ui-kit/package.json > apps/ui-kit/package.temp.json
|
||||
mv apps/ui-kit/package.temp.json apps/ui-kit/package.json
|
||||
|
||||
# Commit changes
|
||||
echo "Committing changes..."
|
||||
git add apps/ui-kit/package.json
|
||||
git commit -m "chore: bump ui-kit version to $VERSION"
|
||||
git push
|
||||
fi
|
||||
|
||||
# Step 5: Push tag
|
||||
if git tag | grep -q "^${NEW_TAG}\$"; then
|
||||
echo "Tag $NEW_TAG already exists."
|
||||
else
|
||||
echo "Creating and pushing tag $NEW_TAG..."
|
||||
git tag "$NEW_TAG"
|
||||
git push origin "$NEW_TAG"
|
||||
fi
|
||||
|
||||
echo "UI Kit release process completed successfully."
|
||||
Reference in New Issue
Block a user